diff options
Diffstat (limited to 'src/main/kotlin')
24 files changed, 155 insertions, 3323 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt index 80279020..de819c98 100644 --- a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt +++ b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt @@ -29,13 +29,9 @@ import com.github.snabbdom.datasetModule import com.github.snabbdom.eventListenersModule import com.github.snabbdom.propsModule import com.github.snabbdom.styleModule -import org.w3c.dom.asList import pl.treksoft.kvision.core.Component -import pl.treksoft.kvision.i18n.I18n import pl.treksoft.kvision.utils.isIE11 -import pl.treksoft.kvision.utils.obj import kotlin.browser.document -import kotlin.browser.window import kotlin.dom.clear /** @@ -49,208 +45,18 @@ external fun require(name: String): dynamic */ @Suppress("EmptyCatchBlock", "TooGenericExceptionCaught", "LargeClass") internal object KVManager { - internal const val AJAX_REQUEST_DELAY = 300 - internal const val KVNULL = "#kvnull" - - private val links = document.getElementsByTagName("link") - private val bootstrapWebpack = try { - val bootswatch = links.asList().find { it.getAttribute("href")?.contains("bootstrap.min.css") ?: false } - if (bootswatch != null) { - if (bootswatch.getAttribute("href")?.contains("/paper/") == true) { - require("./css/paper.css") - } - require("bootstrap-webpack!./js/bootstrap.config.js") - } else { - require("bootstrap-webpack") - } - } catch (e: Throwable) { - } - private val fontAwesomeWebpack = try { - require("font-awesome-webpack") - } catch (e: Throwable) { - } - private val awesomeBootstrapCheckbox = try { - require("awesome-bootstrap-checkbox") - } catch (e: Throwable) { - } - private val bootstrapSelectCss = try { - require("bootstrap-select/dist/css/bootstrap-select.min.css") - } catch (e: Throwable) { - } - private val bootstrapSelect = try { - require("bootstrap-select/dist/js/bootstrap-select.min.js") - require("./js/bootstrap-select-i18n.min.js") - } catch (e: Throwable) { - } - private val bootstrapSelectAjaxCss = try { - require("ajax-bootstrap-select/dist/css/ajax-bootstrap-select.min.css") - } catch (e: Throwable) { - } - private val bootstrapSelectAjax = try { - require("ajax-bootstrap-select/dist/js/ajax-bootstrap-select.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.de-DE.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.es-ES.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.fr-FR.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.it-IT.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ja-JP.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ko-KR.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.nl-NL.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pl-PL.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pt-BR.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ru-RU.min.js") - require("../../js/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.tr-TR.min.js") - } catch (e: Throwable) { - } - private val trixCss = try { - require("trix/dist/trix.css") - } catch (e: Throwable) { - } - private val trix = try { - val trix = require("trix") - window.asDynamic().Trix = trix - trix.config.languages = obj {} - trix.config.languages["en"] = obj {} - for (key in js("Object").keys(trix.config.lang)) { - trix.config.languages["en"][key] = trix.config.lang[key] - } - val orig = trix.config.toolbar.getDefaultHTML - trix.config.toolbar.getDefaultHTML = { - val config = if (trix.config.languages[I18n.language] != undefined) { - trix.config.languages[I18n.language] - } else { - trix.config.languages["en"] - } - for (key in js("Object").keys(trix.config.lang)) { - trix.config.lang[key] = config[key] - } - orig() - } - require("../../js/js/locales/trix/trix.pl.js") - } catch (e: Throwable) { - } - private val bootstrapDateTimePickerCss = try { - require("bootstrap-datetime-picker/css/bootstrap-datetimepicker.min.css") - } catch (e: Throwable) { - } - private val bootstrapDateTimePicker = try { - require("bootstrap-datetime-picker/js/bootstrap-datetimepicker.min.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ar.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.az.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.bg.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.bn.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ca.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.cs.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.da.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.de.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ee.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.el.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.es.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.fi.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.fr.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.he.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.hr.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.hu.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.hy.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.id.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.is.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.it.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ja.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ko.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.lt.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.lv.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.nl.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.no.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.pl.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.pt.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ro.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.rs.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ru.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.sk.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.sl.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.sv.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.th.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.tr.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.ua.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.uk.js") - require("../../js/js/locales/bootstrap-datetime-picker/bootstrap-datetimepicker.zh.js") - } catch (e: Throwable) { - } - private val bootstrapTouchspinCss = try { - require("bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.css") - } catch (e: Throwable) { - } - private val bootstrapTouchspin = try { - require("bootstrap-touchspin/dist/jquery.bootstrap-touchspin.min.js") + private val kvisionBootstrap = try { + require("kvision-bootstrap").pl.treksoft.kvision.KVManagerBootstrap } catch (e: Throwable) { } private val elementResizeEvent = try { require("element-resize-event") } catch (e: Throwable) { } - private val bootstrapFileinputCss = try { - require("bootstrap-fileinput/css/fileinput.min.css") - } catch (e: Throwable) { - } - private val bootstrapFileinputCssFa = try { - require("bootstrap-fileinput/themes/explorer-fa/theme.min.css") - } catch (e: Throwable) { - } - private val bootstrapFileinput = try { - require("bootstrap-fileinput") - require("../../js/js/locales/bootstrap-fileinput/ar.js") - require("../../js/js/locales/bootstrap-fileinput/az.js") - require("../../js/js/locales/bootstrap-fileinput/bg.js") - require("../../js/js/locales/bootstrap-fileinput/ca.js") - require("../../js/js/locales/bootstrap-fileinput/cr.js") - require("../../js/js/locales/bootstrap-fileinput/cs.js") - require("../../js/js/locales/bootstrap-fileinput/da.js") - require("../../js/js/locales/bootstrap-fileinput/de.js") - require("../../js/js/locales/bootstrap-fileinput/el.js") - require("../../js/js/locales/bootstrap-fileinput/es.js") - require("../../js/js/locales/bootstrap-fileinput/et.js") - require("../../js/js/locales/bootstrap-fileinput/fa.js") - require("../../js/js/locales/bootstrap-fileinput/fi.js") - require("../../js/js/locales/bootstrap-fileinput/fr.js") - require("../../js/js/locales/bootstrap-fileinput/gl.js") - require("../../js/js/locales/bootstrap-fileinput/id.js") - require("../../js/js/locales/bootstrap-fileinput/it.js") - require("../../js/js/locales/bootstrap-fileinput/ja.js") - require("../../js/js/locales/bootstrap-fileinput/ka.js") - require("../../js/js/locales/bootstrap-fileinput/ko.js") - require("../../js/js/locales/bootstrap-fileinput/kz.js") - require("../../js/js/locales/bootstrap-fileinput/lt.js") - require("../../js/js/locales/bootstrap-fileinput/nl.js") - require("../../js/js/locales/bootstrap-fileinput/no.js") - require("../../js/js/locales/bootstrap-fileinput/pl.js") - require("../../js/js/locales/bootstrap-fileinput/pt.js") - require("../../js/js/locales/bootstrap-fileinput/ro.js") - require("../../js/js/locales/bootstrap-fileinput/ru.js") - require("../../js/js/locales/bootstrap-fileinput/sk.js") - require("../../js/js/locales/bootstrap-fileinput/sl.js") - require("../../js/js/locales/bootstrap-fileinput/sv.js") - require("../../js/js/locales/bootstrap-fileinput/th.js") - require("../../js/js/locales/bootstrap-fileinput/tr.js") - require("../../js/js/locales/bootstrap-fileinput/uk.js") - require("../../js/js/locales/bootstrap-fileinput/vi.js") - require("../../js/js/locales/bootstrap-fileinput/zh.js") - } catch (e: Throwable) { - } - private val bootstrapFileinputFa = try { - require("bootstrap-fileinput/themes/explorer-fa/theme.min.js") - } catch (e: Throwable) { - } private val resizable = try { require("jquery-resizable-dom") } catch (e: Throwable) { } - private val handlebars = try { - require("handlebars/dist/handlebars.runtime.min.js") - } catch (e: Throwable) { - } - private val jed = try { - require("jed") - } catch (e: Throwable) { - } internal val fecha = require("fecha") private val sdPatch = Snabbdom.init( arrayOf( @@ -259,7 +65,10 @@ internal object KVManager { ) ) private val sdVirtualize = require("snabbdom-virtualize/strings").default - private val styleCss = require("./css/style.css") + private val styleCss = try { + require("./css/style.css") + } catch (e: Throwable) { + } internal fun patch(id: String, vnode: VNode): VNode { val container = document.getElementById(id) diff --git a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt index c4ac8295..d54af6f7 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt @@ -462,13 +462,13 @@ abstract class StyledComponent : Component { return snstyle } - internal fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }) = + protected fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }) = RefreshDelegateProvider<T>(null, refreshFunction) - internal fun <T> refreshOnUpdate(initialValue: T, refreshFunction: ((T) -> Unit) = { this.refresh() }) = + protected fun <T> refreshOnUpdate(initialValue: T, refreshFunction: ((T) -> Unit) = { this.refresh() }) = RefreshDelegateProvider(initialValue, refreshFunction) - internal inner class RefreshDelegateProvider<T>( + protected inner class RefreshDelegateProvider<T>( private val initialValue: T?, private val refreshFunction: (T) -> Unit ) { operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): RefreshDelegate<T> { @@ -477,7 +477,7 @@ abstract class StyledComponent : Component { } } - internal inner class RefreshDelegate<T>(private val refreshFunction: ((T) -> Unit)) { + protected inner class RefreshDelegate<T>(private val refreshFunction: ((T) -> Unit)) { @Suppress("UNCHECKED_CAST") operator fun getValue(thisRef: StyledComponent, property: KProperty<*>): T { val value = propertyValues[property.name] diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index e7b94cba..52f60813 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -84,9 +84,9 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { */ var draggable: Boolean? by refreshOnUpdate() - internal var surroundingSpan by refreshOnUpdate(false) + protected var surroundingSpan by refreshOnUpdate(false) - internal var eventTarget: Widget? = null + var eventTarget: Widget? = null private var vnode: VNode? = null @@ -602,7 +602,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { } } - internal open fun dispatchEvent(type: String, eventInitDict: CustomEventInit): Boolean? { + protected open fun dispatchEvent(type: String, eventInitDict: CustomEventInit): Boolean? { val event = org.w3c.dom.CustomEvent(type, eventInitDict) return this.getElement()?.dispatchEvent(event) } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt index 323d0e1e..b45eed60 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt @@ -26,11 +26,9 @@ import kotlinx.serialization.Mapper import kotlinx.serialization.decode import kotlinx.serialization.json.JSON import kotlinx.serialization.serializer -import pl.treksoft.kvision.form.upload.Upload import pl.treksoft.kvision.i18n.I18n.trans import pl.treksoft.kvision.types.KDate import pl.treksoft.kvision.types.KFile -import pl.treksoft.kvision.utils.getContent import kotlin.js.Json import kotlin.reflect.KProperty1 @@ -255,22 +253,6 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali } /** - * Returns file with the content read. - * @param key key identifier of the control - * @param kFile object identifying the file - * @return KFile object - */ - @Suppress("EXPERIMENTAL_FEATURE_WARNING") - suspend fun getContent( - key: KProperty1<K, List<KFile>?>, - kFile: KFile - ): KFile { - val control = getControl(key) as Upload - val content = control.getNativeFile(kFile)?.getContent() - return kFile.copy(content = content) - } - - /** * Returns current data model as JSON. * @return data model as JSON */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt index a013c076..3811ed82 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt @@ -375,20 +375,6 @@ open class FormPanel<K : Any>( } /** - * Returns an object with the content of the file. - * @param key key identifier of the control - * @param kFile object identifying the file - * @return KFile object - */ - @Suppress("EXPERIMENTAL_FEATURE_WARNING") - suspend fun getContent( - key: KProperty1<K, List<KFile>?>, - kFile: KFile - ): KFile { - return form.getContent(key, kFile) - } - - /** * Returns current data model as JSON. * @return data model as JSON */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt index b4ed127a..fdc1e3af 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt @@ -28,7 +28,6 @@ import pl.treksoft.kvision.form.FieldLabel import pl.treksoft.kvision.form.HelpBlock import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.StringFormControl -import pl.treksoft.kvision.form.select.Select import pl.treksoft.kvision.panel.SimplePanel /** @@ -99,7 +98,7 @@ open class RadioGroup( setSizeToChildren(value) } - private val idc = "kv_form_radiogroup_" + Select.counter + private val idc = "kv_form_radiogroup_$counter" final override val input = CheckInput() final override val flabel: FieldLabel = FieldLabel(idc, label, rich) final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt deleted file mode 100644 index d802a111..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.KVManager.AJAX_REQUEST_DELAY -import pl.treksoft.kvision.KVManager.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, val preprocessData: (dynamic) -> dynamic, val beforeSend: ((JQueryXHR) -> 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 -) - -/** - * 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(data) - if (emptyOption) { - val ret = mutableListOf(obj { - this.value = KVNULL - this.text = "" - }) - @Suppress("UnsafeCastFromDynamic") - ret.addAll((processedData as Array<dynamic>).asList()) - ret.toTypedArray() - } else { - processedData - } - } - val language = I18n.language - return obj { - this.ajax = obj { - this.url = url - this.type = httpType.type - this.dataType = dataType.type - this.data = data - this.beforeSend = beforeSend - } - this.preprocessData = procData - this.minLength = minLength - this.cache = cache - this.clearOnEmpty = clearOnEmpty - this.clearOnError = clearOnError - this.emptyRequest = emptyRequest - this.preserveSelected = false - this.requestDelay = requestDelay - this.restoreOnError = restoreOnError - this.langCode = language - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt deleted file mode 100644 index f19081e1..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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.HelpBlock -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 (label to value 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<StringPair>? = 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 (label to value 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 validationInfo: HelpBlock = HelpBlock().apply { visible = false } - - init { - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(flabel) - this.addInternal(input) - this.addInternal(validationInfo) - counter++ - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - if (validatorError != null) { - cl.add("has-error" 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 Container.select( - options: List<StringPair>? = 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/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt deleted file mode 100644 index 6d6f0fc5..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ /dev/null @@ -1,363 +0,0 @@ -/* - * 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.KVManager.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.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 (label to value 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<StringPair>? = null, value: String? = null, - multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, - classes: Set<String> = setOf() -) : SimplePanel(classes), FormInput { - - /** - * A list of options (label to value pairs) for the select control. - */ - internal 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() - - init { - setChildrenFromOptions() - this.setInternalEventListener<SelectInput> { - 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<String> - if (arr != null && arr.isNotEmpty()) { - arr.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<Component>): 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<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - cl.add("selectpicker" to true) - size?.let { - cl.add(it.className to true) - } - return cl - } - - private 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 "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() { - value?.let { - if (multiple) { - getElementJQueryD()?.selectpicker("val", it.split(",").toTypedArray()) - } else { - getElementJQueryD()?.selectpicker("val", it) - } - } ?: getElementJQueryD()?.selectpicker("val", null) - } - - /** - * Makes the input element focused. - */ - open fun focus() { - getElementJQuery()?.focus() - } - - /** - * Makes the input element blur. - */ - open 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<StringPair>? = null, value: String? = null, - multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, - classes: Set<String> = 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/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt deleted file mode 100644 index e33b3457..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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<StringPair>? = null, maxOptions: Int? = null, - disabled: Boolean = false, classes: Set<String> = 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<StringPair> { - 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 - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt deleted file mode 100644 index d1bb636e..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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, - classes: Set<String> = 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) - - override fun render(): VNode { - return if (!divider) { - render("option", arrayOf(translate(label) ?: value)) - } else { - render("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 translate(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 "disabled") - } - } else { - sn.add("data-divider" to "true") - } - return sn - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt deleted file mode 100644 index 4fa68e47..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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.spinner - -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.HelpBlock -import pl.treksoft.kvision.form.NumberFormControl -import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.utils.SnOn - -/** - * The form field component for spinner control. - * - * @constructor - * @param value spinner value - * @param name the name attribute of the generated HTML input element - * @param min minimal value (default 0) - * @param max maximal value (default 100) - * @param step step value (default 1) - * @param decimals number of decimal digits (default 0) - * @param buttonsType spinner buttons type - * @param forceType spinner force rounding type - * @param label label text bound to the input element - * @param rich determines if [label] can contain HTML code - */ -open class Spinner( - value: Number? = null, name: String? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP, - decimals: Int = 0, buttonsType: ButtonsType = ButtonsType.VERTICAL, - forceType: ForceType = ForceType.NONE, label: String? = null, - rich: Boolean = false -) : SimplePanel(setOf("form-group")), NumberFormControl { - - /** - * Spinner value. - */ - override var value - get() = input.value - set(value) { - input.value = value - } - /** - * The value attribute of the generated HTML input element. - * - * This value is placed directly in generated HTML code, while the [value] property is dynamically - * bound to the spinner input value. - */ - var startValue - get() = input.startValue - set(value) { - input.startValue = value - } - /** - * Minimal value. - */ - var min - get() = input.min - set(value) { - input.min = value - } - /** - * Maximal value. - */ - var max - get() = input.max - set(value) { - input.max = value - } - /** - * Step value. - */ - var step - get() = input.step - set(value) { - input.step = value - } - /** - * Number of decimal digits value. - */ - var decimals - get() = input.decimals - set(value) { - input.decimals = value - } - /** - * Spinner buttons type. - */ - var buttonsType - get() = input.buttonsType - set(value) { - input.buttonsType = value - } - /** - * Spinner force rounding type. - */ - var forceType - get() = input.forceType - set(value) { - input.forceType = value - } - /** - * The placeholder for the spinner input. - */ - var placeholder - get() = input.placeholder - set(value) { - input.placeholder = value - } - /** - * Determines if the spinner is automatically focused. - */ - var autofocus - get() = input.autofocus - set(value) { - input.autofocus = value - } - /** - * Determines if the spinner is read-only. - */ - var readonly - get() = input.readonly - set(value) { - input.readonly = value - } - /** - * The label text bound to the spinner input 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 - } - - protected val idc = "kv_form_spinner_$counter" - final override val input: SpinnerInput = SpinnerInput(value, min, max, step, decimals, buttonsType, forceType) - .apply { - this.id = idc - this.name = name - } - final override val flabel: FieldLabel = FieldLabel(idc, label, rich) - final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } - - init { - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(flabel) - this.addInternal(input) - this.addInternal(validationInfo) - counter++ - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - if (validatorError != null) { - cl.add("has-error" 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 getValueAsString(): String? { - return input.getValueAsString() - } - - /** - * Change value in plus. - */ - open fun spinUp(): Spinner { - input.spinUp() - return this - } - - /** - * Change value in minus. - */ - open fun spinDown(): Spinner { - input.spinDown() - return this - } - - 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.spinner( - value: Number? = null, - name: String? = null, - min: Int = 0, - max: Int = DEFAULT_MAX, - step: Double = DEFAULT_STEP, - decimals: Int = 0, - buttonsType: ButtonsType = ButtonsType.VERTICAL, - forceType: ForceType = ForceType.NONE, - label: String? = null, - rich: Boolean = false, - init: (Spinner.() -> Unit)? = null - ): Spinner { - val spinner = Spinner(value, name, min, max, step, decimals, buttonsType, forceType, label, rich).apply { - init?.invoke( - this - ) - } - this.add(spinner) - return spinner - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt deleted file mode 100644 index 7d3af684..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt +++ /dev/null @@ -1,323 +0,0 @@ -/* - * 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.spinner - -import com.github.snabbdom.VNode -import pl.treksoft.jquery.JQuery -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.FormInput -import pl.treksoft.kvision.form.InputSize -import pl.treksoft.kvision.utils.obj - -/** - * Spinner buttons layout types. - */ -enum class ButtonsType { - NONE, - HORIZONTAL, - VERTICAL -} - -/** - * Spinner force rounding types. - */ -enum class ForceType(internal val value: String) { - NONE("none"), - ROUND("round"), - FLOOR("floor"), - CEIL("cail") -} - -internal const val DEFAULT_STEP = 1.0 -internal const val DEFAULT_MAX = 100 - -/** - * The basic component for spinner control. - * - * @constructor - * @param value spinner value - * @param min minimal value (default 0) - * @param max maximal value (default 100) - * @param step step value (default 1) - * @param decimals number of decimal digits (default 0) - * @param buttonsType spinner buttons type - * @param forceType spinner force rounding type - * @param classes a set of CSS class names - */ -@Suppress("TooManyFunctions") -open class SpinnerInput( - value: Number? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP, - decimals: Int = 0, buttonsType: ButtonsType = ButtonsType.VERTICAL, - forceType: ForceType = ForceType.NONE, - classes: Set<String> = setOf() -) : Widget(classes + "form-control"), FormInput { - - init { - this.addSurroundingCssClass("input-group") - if (buttonsType == ButtonsType.NONE) { - this.addSurroundingCssClass("kv-spinner-btn-none") - } else { - this.removeSurroundingCssClass("kv-spinner-btn-none") - } - if (buttonsType == ButtonsType.VERTICAL) { - this.addSurroundingCssClass("kv-spinner-btn-vertical") - } else { - this.removeSurroundingCssClass("kv-spinner-btn-vertical") - } - this.surroundingSpan = true - this.refreshSpinner() - this.setInternalEventListener<SpinnerInput> { - change = { - self.changeValue() - } - } - } - - /** - * Spinner value. - */ - var value by refreshOnUpdate(value, { refreshState() }) - /** - * The value attribute of the generated HTML input element. - * - * This value is placed directly in generated HTML code, while the [value] property is dynamically - * bound to the spinner input value. - */ - var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) - /** - * Minimal value. - */ - var min by refreshOnUpdate(min, { refreshSpinner() }) - /** - * Maximal value. - */ - var max by refreshOnUpdate(max, { refreshSpinner() }) - /** - * Step value. - */ - var step by refreshOnUpdate(step, { refreshSpinner() }) - /** - * Number of decimal digits value. - */ - var decimals by refreshOnUpdate(decimals, { refreshSpinner() }) - /** - * Spinner buttons type. - */ - var buttonsType by refreshOnUpdate(buttonsType, { refreshSpinner() }) - /** - * Spinner force rounding type. - */ - var forceType by refreshOnUpdate(forceType, { refreshSpinner() }) - /** - * The placeholder for the spinner input. - */ - var placeholder: String? by refreshOnUpdate() - /** - * The name attribute of the generated HTML input element. - */ - override var name: String? by refreshOnUpdate() - /** - * Determines if the field is disabled. - */ - override var disabled by refreshOnUpdate(false) - /** - * Determines if the spinner is automatically focused. - */ - var autofocus: Boolean? by refreshOnUpdate() - /** - * Determines if the spinner is read-only. - */ - var readonly: Boolean? by refreshOnUpdate() - /** - * The size of the input. - */ - override var size: InputSize? by refreshOnUpdate() - - private var siblings: JQuery? = null - - override fun render(): VNode { - return render("input") - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - size?.let { - cl.add(it.className to true) - } - return cl - } - - @Suppress("ComplexMethod") - override fun getSnAttrs(): List<StringPair> { - val sn = super.getSnAttrs().toMutableList() - sn.add("type" to "text") - startValue?.let { - sn.add("value" to it.toString()) - } - placeholder?.let { - sn.add("placeholder" to translate(it)) - } - name?.let { - sn.add("name" to it) - } - autofocus?.let { - if (it) { - sn.add("autofocus" to "autofocus") - } - } - readonly?.let { - if (it) { - sn.add("readonly" to "readonly") - } - } - if (disabled) { - sn.add("disabled" to "disabled") - value?.let { - sn.add("value" to it.toString()) - } - } - return sn - } - - protected open fun changeValue() { - val v = getElementJQuery()?.`val`() as String? - if (v != null && v.isNotEmpty()) { - this.value = v.toDoubleOrNull() - } else { - this.value = null - } - } - - @Suppress("UnsafeCastFromDynamic") - override fun afterInsert(node: VNode) { - getElementJQueryD()?.TouchSpin(getSettingsObj()) - siblings = getElementJQuery()?.parent(".bootstrap-touchspin")?.children("span") - size?.let { - siblings?.find("button")?.addClass(it.className) - } - this.getElementJQuery()?.on("change", { e, _ -> - if (e.asDynamic().isTrigger != null) { - val event = org.w3c.dom.events.Event("change") - this.getElement()?.dispatchEvent(event) - } - }) - this.getElementJQuery()?.on("touchspin.on.min", { e, _ -> - this.dispatchEvent("onMinBsSpinner", obj { detail = e }) - }) - this.getElementJQuery()?.on("touchspin.on.max", { e, _ -> - this.dispatchEvent("onMaxBsSpinner", obj { detail = e }) - }) - refreshState() - } - - override fun afterDestroy() { - siblings?.remove() - siblings = null - } - - /** - * Returns the value of the spinner as a String. - * @return value as a String - */ - fun getValueAsString(): String? { - return value?.toString() - } - - /** - * Change value in plus. - */ - fun spinUp(): SpinnerInput { - getElementJQueryD()?.trigger("touchspin.uponce") - return this - } - - /** - * Change value in minus. - */ - fun spinDown(): SpinnerInput { - getElementJQueryD()?.trigger("touchspin.downonce") - return this - } - - private fun refreshState() { - value?.let { - getElementJQuery()?.`val`(it.toString()) - } ?: getElementJQueryD()?.`val`(null) - } - - private fun refreshSpinner() { - getElementJQueryD()?.trigger("touchspin.updatesettings", getSettingsObj()) - } - - private fun getSettingsObj(): dynamic { - val verticalbuttons = buttonsType == ButtonsType.VERTICAL || buttonsType == ButtonsType.NONE - return obj { - this.min = min - this.max = max - this.step = step - this.decimals = decimals - this.verticalbuttons = verticalbuttons - this.forcestepdivisibility = forceType.value - } - } - - /** - * Makes the input element focused. - */ - open fun focus() { - getElementJQuery()?.focus() - } - - /** - * Makes the input element blur. - */ - open fun blur() { - getElementJQuery()?.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.spinnerInput( - value: Number? = null, min: Int = 0, max: Int = DEFAULT_MAX, step: Double = DEFAULT_STEP, - decimals: Int = 0, buttonsType: ButtonsType = ButtonsType.VERTICAL, - forceType: ForceType = ForceType.NONE, classes: Set<String> = setOf(), - init: (SpinnerInput.() -> Unit)? = null - ): SpinnerInput { - val spinnerInput = SpinnerInput(value, min, max, step, decimals, buttonsType, forceType, classes).apply { - init?.invoke( - this - ) - } - this.add(spinnerInput) - return spinnerInput - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt deleted file mode 100644 index 22126797..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 - -/** - * Form field rich text component. - * - * @constructor - * @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 RichText( - value: String? = null, name: String? = null, - label: String? = null, rich: Boolean = false -) : AbstractText(label, rich) { - - /** - * Rich input control height. - */ - var inputHeight - get() = input.height - set(value) { - input.height = value - } - - final override val input: RichTextInput = RichTextInput(value).apply { - this.id = idc - this.name = name - } - - init { - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(input) - this.addInternal(validationInfo) - } - - companion object { - /** - * DSL builder extension function. - * - * It takes the same parameters as the constructor of the built component. - */ - fun Container.richText( - value: String? = null, - name: String? = null, - label: String? = null, - rich: Boolean = false, - init: (RichText.() -> Unit)? = null - ): RichText { - val richText = RichText(value, name, label, rich).apply { init?.invoke(this) } - this.add(richText) - return richText - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt deleted file mode 100644 index c5d9a556..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/RichTextInput.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 com.github.snabbdom.VNode -import pl.treksoft.jquery.jQuery -import pl.treksoft.kvision.core.Container -import pl.treksoft.kvision.core.StringPair -import kotlin.browser.document - -/** - * Basic rich text component. - * - * @constructor - * @param value text input value - * @param classes a set of CSS class names - */ -open class RichTextInput(value: String? = null, classes: Set<String> = setOf()) : - AbstractTextInput(value, classes + "form-control" + "trix-control") { - - private var trixId: String? = null - - override fun render(): VNode { - return render("trix-editor") - } - - override fun getSnAttrs(): List<StringPair> { - val sn = super.getSnAttrs().toMutableList() - placeholder?.let { - sn.add("placeholder" to translate(it)) - } - name?.let { - sn.add("name" to it) - } - autofocus?.let { - if (it) { - sn.add("autofocus" to "autofocus") - } - } - if (disabled) { - sn.add("disabled" to "disabled") - } - return sn - } - - @Suppress("UnsafeCastFromDynamic", "ComplexMethod") - override fun afterInsert(node: VNode) { - if (this.disabled || this.readonly == true) { - this.getElementJQuery()?.removeAttr("contenteditable") - } else { - this.getElementJQuery()?.on("trix-change", { _, _ -> - if (trixId != null) { - val v = document.getElementById("trix-input-$trixId")?.let { jQuery(it).`val`() as String? } - value = if (v != null && v.isNotEmpty()) { - v - } else { - null - } - val event = org.w3c.dom.events.Event("change") - this.getElement()?.dispatchEvent(event) - } - }) - } - this.getElementJQuery()?.on("trix-initialize", { _, _ -> - trixId = this.getElementJQuery()?.attr("trix-id") - if (trixId != null) { - value?.let { - if (this.getElement().asDynamic().editor != undefined) { - this.getElement().asDynamic().editor.loadHTML(it) - } - } - } - }) - this.getElementJQuery()?.on("trix-file-accept", { e, _ -> e.preventDefault() }) - } - - override fun afterDestroy() { - document.getElementById("trix-input-$trixId")?.let { jQuery(it).remove() } - document.getElementById("trix-toolbar-$trixId")?.let { jQuery(it).remove() } - trixId = null - } - - @Suppress("UnsafeCastFromDynamic") - override fun refreshState() { - val v = document.getElementById("trix-input-$trixId")?.let { jQuery(it).`val`() as String? } - if (value != v) { - val editor = this.getElement().asDynamic().editor - if (editor != undefined) { - value?.let { - editor.loadHTML(it) - } ?: editor.loadHTML("") - } - } - } - - override fun changeValue() { - // disabled parent class functionality - } - - companion object { - /** - * DSL builder extension function. - * - * It takes the same parameters as the constructor of the built component. - */ - fun Container.richTextInput( - value: String? = null, classes: Set<String> = setOf(), init: (RichTextInput.() -> Unit)? = null - ): RichTextInput { - val richTextInput = RichTextInput(value, classes).apply { init?.invoke(this) } - this.add(richTextInput) - return richTextInput - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt deleted file mode 100644 index 9cdd0369..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt +++ /dev/null @@ -1,246 +0,0 @@ -/* - * 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.time - -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.HelpBlock -import pl.treksoft.kvision.form.KDateFormControl -import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.types.KDate -import pl.treksoft.kvision.utils.SnOn - -/** - * Form field date/time chooser component. - * - * @constructor - * @param value date/time input value - * @param name the name attribute of the generated HTML input element - * @param format date/time format (default YYYY-MM-DD HH:mm) - * @param label label text bound to the input element - * @param rich determines if [label] can contain HTML code - */ -open class DateTime( - value: KDate? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, - rich: Boolean = false -) : SimplePanel(setOf("form-group")), KDateFormControl { - - /** - * Date/time input value. - */ - override var value - get() = input.value - set(value) { - input.value = value - } - /** - * Date/time format. - */ - var format - get() = input.format - set(value) { - input.format = value - } - /** - * The placeholder for the date/time input. - */ - var placeholder - get() = input.placeholder - set(value) { - input.placeholder = value - } - /** - * Determines if the date/time input is automatically focused. - */ - var autofocus - get() = input.autofocus - set(value) { - input.autofocus = value - } - /** - * Determines if the date/time input is read-only. - */ - var readonly - get() = input.readonly - set(value) { - input.readonly = value - } - /** - * Day of the week start. 0 (Sunday) to 6 (Saturday). - */ - var weekStart - get() = input.weekStart - set(value) { - input.weekStart = value - } - /** - * Days of the week that should be disabled. Multiple values should be comma separated. - */ - var daysOfWeekDisabled - get() = input.daysOfWeekDisabled - set(value) { - input.daysOfWeekDisabled = value - } - /** - * Determines if *Clear* button should be visible. - */ - var clearBtn - get() = input.clearBtn - set(value) { - input.clearBtn = value - } - /** - * Determines if *Today* button should be visible. - */ - var todayBtn - get() = input.todayBtn - set(value) { - input.todayBtn = value - } - /** - * Determines if the current day should be highlighted. - */ - var todayHighlight - get() = input.todayHighlight - set(value) { - input.todayHighlight = value - } - /** - * The increment used to build the hour view. - */ - var minuteStep - get() = input.minuteStep - set(value) { - input.minuteStep = value - } - /** - * Determines if meridian views are visible in day and hour views. - */ - var showMeridian - get() = input.showMeridian - set(value) { - input.showMeridian = value - } - /** - * The label text bound to the input 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_time_$counter" - final override val input: DateTimeInput = DateTimeInput(value, format).apply { - this.id = idc - this.name = name - } - final override val flabel: FieldLabel = FieldLabel(idc, label, rich) - final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } - - init { - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(flabel) - this.addInternal(input) - this.addInternal(validationInfo) - counter++ - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - if (validatorError != null) { - cl.add("has-error" 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 - } - - /** - * Open date/time chooser popup. - */ - open fun showPopup() { - input.showPopup() - } - - /** - * Hides date/time chooser popup. - */ - open fun hidePopup() { - input.hidePopup() - } - - override fun getValueAsString(): String? { - return input.getValueAsString() - } - - 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.dateTime( - value: KDate? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, - rich: Boolean = false, init: (DateTime.() -> Unit)? = null - ): DateTime { - val dateTime = DateTime(value, name, format, label, rich).apply { init?.invoke(this) } - this.add(dateTime) - return dateTime - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt deleted file mode 100644 index 1df8a082..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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.time - -import com.github.snabbdom.VNode -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.FormInput -import pl.treksoft.kvision.form.InputSize -import pl.treksoft.kvision.i18n.I18n -import pl.treksoft.kvision.types.KDate -import pl.treksoft.kvision.types.toJS -import pl.treksoft.kvision.types.toKDateF -import pl.treksoft.kvision.types.toStringF -import pl.treksoft.kvision.utils.obj - -internal const val DEFAULT_MINUTE_STEP = 5 -internal const val MAX_VIEW = 4 - -/** - * Basic date/time chooser component. - * - * @constructor - * @param value date/time input value - * @param format date/time format (default YYYY-MM-DD HH:mm) - * @param classes a set of CSS class names - */ -@Suppress("TooManyFunctions") -open class DateTimeInput( - value: KDate? = null, format: String = "YYYY-MM-DD HH:mm", - classes: Set<String> = setOf() -) : Widget(classes + "form-control"), FormInput { - - init { - this.setInternalEventListener<DateTimeInput> { - change = { - self.changeValue() - } - } - } - - /** - * Date/time input value. - */ - var value by refreshOnUpdate(value, { refreshState() }) - /** - * Date/time format. - */ - var format by refreshOnUpdate(format, { refreshDatePicker() }) - /** - * The placeholder for the date/time input. - */ - var placeholder: String? by refreshOnUpdate() - /** - * The name attribute of the generated HTML input element. - */ - override var name: String? by refreshOnUpdate() - /** - * Determines if the field is disabled. - */ - override var disabled by refreshOnUpdate(false) - /** - * Determines if the text input is automatically focused. - */ - var autofocus: Boolean? by refreshOnUpdate() - /** - * Determines if the date/time input is read-only. - */ - var readonly: Boolean? by refreshOnUpdate() - /** - * The size of the input. - */ - override var size: InputSize? by refreshOnUpdate() - /** - * Day of the week start. 0 (Sunday) to 6 (Saturday). - */ - var weekStart by refreshOnUpdate(0, { refreshDatePicker() }) - /** - * Days of the week that should be disabled. Multiple values should be comma separated. - */ - var daysOfWeekDisabled by refreshOnUpdate(arrayOf<Int>(), { refreshDatePicker() }) - /** - * Determines if *Clear* button should be visible. - */ - var clearBtn by refreshOnUpdate(true, { refreshDatePicker() }) - /** - * Determines if *Today* button should be visible. - */ - var todayBtn by refreshOnUpdate(false, { refreshDatePicker() }) - /** - * Determines if the current day should be highlighted. - */ - var todayHighlight by refreshOnUpdate(false, { refreshDatePicker() }) - /** - * The increment used to build the hour view. - */ - var minuteStep by refreshOnUpdate(DEFAULT_MINUTE_STEP, { refreshDatePicker() }) - /** - * Determines if meridian views are visible in day and hour views. - */ - var showMeridian by refreshOnUpdate(false, { refreshDatePicker() }) - - override fun render(): VNode { - return render("input") - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - size?.let { - cl.add(it.className to true) - } - return cl - } - - override fun getSnAttrs(): List<StringPair> { - val sn = super.getSnAttrs().toMutableList() - sn.add("type" to "text") - placeholder?.let { - sn.add("placeholder" to translate(it)) - } - name?.let { - sn.add("name" to it) - } - autofocus?.let { - if (it) { - sn.add("autofocus" to "autofocus") - } - } - readonly?.let { - if (it) { - sn.add("readonly" to "readonly") - } - } - if (disabled) { - sn.add("disabled" to "disabled") - value?.let { - sn.add("value" to it.toStringF(format)) - } - } - return sn - } - - @Suppress("UnsafeCastFromDynamic") - protected open fun refreshState() { - value?.let { - getElementJQueryD()?.datetimepicker("update", it.toJS()) - } ?: run { - getElementJQueryD()?.`val`(null) - getElementJQueryD()?.datetimepicker("update", null) - } - } - - protected open fun refreshDatePicker() { - getElementJQueryD()?.`val`(null) - getElementJQueryD()?.datetimepicker("remove") - initDateTimePicker() - refreshState() - } - - protected open fun changeValue() { - val v = getElementJQuery()?.`val`() as String? - if (v != null && v.isNotEmpty()) { - this.value = v.toKDateF(format) - } else { - this.value = null - } - } - - /** - * Open date/time chooser popup. - */ - open fun showPopup() { - getElementJQueryD()?.datetimepicker("show") - } - - /** - * Hides date/time chooser popup. - */ - open fun hidePopup() { - getElementJQueryD()?.datetimepicker("hide") - } - - @Suppress("UnsafeCastFromDynamic") - override fun afterInsert(node: VNode) { - if (!this.disabled) { - this.initDateTimePicker() - this.getElementJQuery()?.on("changeDate", { e, _ -> - this.dispatchEvent("change", obj { detail = e }) - }) - this.getElementJQuery()?.on("show", { e, _ -> - this.dispatchEvent("showBsDateTime", obj { detail = e }) - }) - this.getElementJQuery()?.on("hide", { e, _ -> - this.dispatchEvent("hideBsDateTime", obj { detail = e }) - }) - refreshState() - } - } - - override fun afterDestroy() { - getElementJQueryD()?.datetimepicker("remove") - } - - private fun initDateTimePicker() { - val datePickerFormat = format.toDatePickerFormat() - val minView = if (format.contains("HH") || format.contains("mm")) 0 else 2 - val maxView = if (format.contains("YY") || format.contains("M") || format.contains("D")) MAX_VIEW else 1 - val startView = if (maxView < 2) maxView else 2 - val language = I18n.language - getElementJQueryD()?.datetimepicker(obj { - this.format = datePickerFormat - this.startView = startView - this.minView = minView - this.maxView = maxView - this.minuteStep = minuteStep - this.todayHighlight = todayHighlight - this.clearBtn = clearBtn - this.todayBtn = todayBtn - this.weekStart = weekStart - this.showMeridian = showMeridian - this.daysOfWeekDisabled = daysOfWeekDisabled - this.autoclose = true - this.language = language - }) - } - - /** - * Get value of date/time input control as String - * @return value as a String - */ - fun getValueAsString(): String? { - return value?.toStringF(format) - } - - /** - * Makes the input element focused. - */ - open fun focus() { - getElementJQuery()?.focus() - } - - /** - * Makes the input element blur. - */ - open fun blur() { - getElementJQuery()?.blur() - } - - companion object { - private fun String.toDatePickerFormat(): String { - return this.replace("YY", "yy").replace("m", "i").replace("MMMM", "{----}").replace("MMM", "{---}") - .replace("M", "m").replace("{----}", "MM").replace("{---}", "M").replace("H", "{-}") - .replace("h", "H").replace("{-}", "h").replace("D", "d").replace("a", "p").replace("A", "P") - } - - /** - * DSL builder extension function. - * - * It takes the same parameters as the constructor of the built component. - */ - fun Container.dateTimeInput( - value: KDate? = null, format: String = "YYYY-MM-DD HH:mm", classes: Set<String> = setOf(), - init: (DateTimeInput.() -> Unit)? = null - ): DateTimeInput { - val dateTimeInput = DateTimeInput(value, format, classes).apply { init?.invoke(this) } - this.add(dateTimeInput) - return dateTimeInput - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/upload/Upload.kt b/src/main/kotlin/pl/treksoft/kvision/form/upload/Upload.kt deleted file mode 100644 index 314c9904..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/upload/Upload.kt +++ /dev/null @@ -1,333 +0,0 @@ -/* - * 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.upload - -import org.w3c.files.File -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.HelpBlock -import pl.treksoft.kvision.form.KFilesFormControl -import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.types.KFile -import pl.treksoft.kvision.utils.SnOn - -/** - * The form field file upload component. - * - * @constructor - * @param uploadUrl the optional URL for the upload processing action - * @param multiple determines if multiple file upload is supported - * @param label label text bound to the input element - * @param rich determines if [label] can contain HTML code - */ -@Suppress("TooManyFunctions") -open class Upload( - uploadUrl: String? = null, multiple: Boolean = false, label: String? = null, - rich: Boolean = false -) : SimplePanel(setOf("form-group")), KFilesFormControl { - - /** - * File input value. - */ - override var value - get() = input.value - set(value) { - input.value = value - } - /** - * The optional URL for the upload processing action. - * If not set the upload button action will default to form submission. - */ - var uploadUrl - get() = input.uploadUrl - set(value) { - input.uploadUrl = value - } - /** - * Determines if multiple file upload is supported. - */ - var multiple - get() = input.multiple - set(value) { - input.multiple = value - } - /** - * The extra data that will be passed as data to the AJAX server call via POST. - */ - var uploadExtraData - get() = input.uploadExtraData - set(value) { - input.uploadExtraData = value - } - /** - * Determines if the explorer theme is used. - */ - var explorerTheme - get() = input.explorerTheme - set(value) { - input.explorerTheme = value - } - /** - * Determines if the input selection is required. - */ - var required - get() = input.required - set(value) { - input.required = value - } - /** - * Determines if the caption is shown. - */ - var showCaption - get() = input.showCaption - set(value) { - input.showCaption = value - } - /** - * Determines if the preview is shown. - */ - var showPreview - get() = input.showPreview - set(value) { - input.showPreview = value - } - /** - * Determines if the remove button is shown. - */ - var showRemove - get() = input.showRemove - set(value) { - input.showRemove = value - } - /** - * Determines if the upload button is shown. - */ - var showUpload - get() = input.showUpload - set(value) { - input.showUpload = value - } - /** - * Determines if the cancel button is shown. - */ - var showCancel - get() = input.showCancel - set(value) { - input.showCancel = value - } - /** - * Determines if the file browse button is shown. - */ - var showBrowse - get() = input.showBrowse - set(value) { - input.showBrowse = value - } - /** - * Determines if the click on the preview zone opens file browse window. - */ - var browseOnZoneClick - get() = input.browseOnZoneClick - set(value) { - input.browseOnZoneClick = value - } - /** - * Determines if the iconic preview is prefered. - */ - var preferIconicPreview - get() = input.preferIconicPreview - set(value) { - input.preferIconicPreview = value - } - /** - * Allowed file types. - */ - var allowedFileTypes - get() = input.allowedFileTypes - set(value) { - input.allowedFileTypes = value - } - /** - * Allowed file extensions. - */ - var allowedFileExtensions - get() = input.allowedFileExtensions - set(value) { - input.allowedFileExtensions = value - } - /** - * Determines if Drag&Drop zone is enabled. - */ - var dropZoneEnabled - get() = input.dropZoneEnabled - set(value) { - input.dropZoneEnabled = value - } - /** - * The label text bound to the spinner input 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 - } - - protected val idc = "kv_form_upload_$counter" - final override val input: UploadInput = UploadInput(uploadUrl, multiple) - .apply { - this.id = idc - this.name = name - } - final override val flabel: FieldLabel = FieldLabel(idc, label, rich) - final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } - - init { - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(flabel) - this.addInternal(input) - this.addInternal(validationInfo) - counter++ - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - if (validatorError != null) { - cl.add("has-error" 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 getValueAsString(): String? { - return input.getValueAsString() - } - - /** - * Returns the native JavaScript File object. - * @param kFile KFile object - * @return File object - */ - fun getNativeFile(kFile: KFile): File? { - return input.getNativeFile(kFile) - } - - /** - * Resets the file input control. - */ - open fun resetInput() { - input.resetInput() - } - - /** - * Clears the file input control (including the native input). - */ - open fun clearInput() { - input.clearInput() - } - - /** - * Trigger ajax upload (only for ajax mode). - */ - open fun upload() { - input.upload() - } - - /** - * Cancel an ongoing ajax upload (only for ajax mode). - */ - open fun cancel() { - input.cancel() - } - - /** - * Locks the file input (disabling all buttons except a cancel button). - */ - open fun lock() { - input.lock() - } - - /** - * Unlocks the file input. - */ - open fun unlock() { - input.unlock() - } - - 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.upload( - uploadUrl: String? = null, - multiple: Boolean = false, - label: String? = null, - rich: Boolean = false, - init: (Upload.() -> Unit)? = null - ): Upload { - val upload = Upload(uploadUrl, multiple, label, rich).apply { - init?.invoke( - this - ) - } - this.add(upload) - return upload - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt deleted file mode 100644 index 0f8aaf4a..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/upload/UploadInput.kt +++ /dev/null @@ -1,327 +0,0 @@ -/* - * 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.upload - -import com.github.snabbdom.VNode -import org.w3c.files.File -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.FormInput -import pl.treksoft.kvision.form.InputSize -import pl.treksoft.kvision.i18n.I18n -import pl.treksoft.kvision.types.KFile -import pl.treksoft.kvision.utils.obj - -/** - * The file upload component. - * - * @constructor - * @param uploadUrl the optional URL for the upload processing action - * @param multiple determines if multiple file upload is supported - * @param classes a set of CSS class names - */ -@Suppress("TooManyFunctions") -open class UploadInput(uploadUrl: String? = null, multiple: Boolean = false, classes: Set<String> = setOf()) : - Widget(classes + "form-control"), FormInput { - - /** - * File input value. - */ - var value: List<KFile>? - get() = getValue() - set(value) { - if (value == null) resetInput() - } - - /** - * The optional URL for the upload processing action. - * If not set the upload button action will default to form submission. - */ - var uploadUrl: String? by refreshOnUpdate(uploadUrl, { refreshUploadInput() }) - /** - * Determines if multiple file upload is supported. - */ - var multiple: Boolean by refreshOnUpdate(multiple, { refresh(); refreshUploadInput() }) - /** - * The extra data that will be passed as data to the AJAX server call via POST. - */ - var uploadExtraData: ((String, Int) -> dynamic)? by refreshOnUpdate({ refreshUploadInput() }) - /** - * Determines if the explorer theme is used. - */ - var explorerTheme: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) - /** - * Determines if the input selection is required. - */ - var required: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) - /** - * Determines if the caption is shown. - */ - var showCaption: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the preview is shown. - */ - var showPreview: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the remove button is shown. - */ - var showRemove: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the upload button is shown. - */ - var showUpload: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the cancel button is shown. - */ - var showCancel: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the file browse button is shown. - */ - var showBrowse: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the click on the preview zone opens file browse window. - */ - var browseOnZoneClick: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * Determines if the iconic preview is prefered. - */ - var preferIconicPreview: Boolean by refreshOnUpdate(false, { refreshUploadInput() }) - /** - * Allowed file types. - */ - var allowedFileTypes: Set<String>? by refreshOnUpdate({ refreshUploadInput() }) - /** - * Allowed file extensions. - */ - var allowedFileExtensions: Set<String>? by refreshOnUpdate({ refreshUploadInput() }) - /** - * Determines if Drag&Drop zone is enabled. - */ - var dropZoneEnabled: Boolean by refreshOnUpdate(true, { refreshUploadInput() }) - /** - * The name attribute of the generated HTML input element. - */ - override var name: String? by refreshOnUpdate() - /** - * Determines if the field is disabled. - */ - override var disabled by refreshOnUpdate(false, { refresh(); refreshUploadInput() }) - /** - * The size of the input (currently not working) - */ - override var size: InputSize? by refreshOnUpdate() - - private val nativeFiles: MutableMap<KFile, File> = mutableMapOf() - - override fun render(): VNode { - return render("input") - } - - override fun getSnClass(): List<StringBoolPair> { - val cl = super.getSnClass().toMutableList() - size?.let { - cl.add(it.className to true) - } - return cl - } - - override fun getSnAttrs(): List<StringPair> { - val sn = super.getSnAttrs().toMutableList() - sn.add("type" to "file") - name?.let { - sn.add("name" to it) - } - if (multiple) { - sn.add("multiple" to "true") - } - if (disabled) { - sn.add("disabled" to "disabled") - } - return sn - } - - private fun getValue(): List<KFile>? { - val v = getFiles() - return if (v.isNotEmpty()) v else null - } - - @Suppress("UnsafeCastFromDynamic") - override fun afterInsert(node: VNode) { - getElementJQueryD()?.fileinput(getSettingsObj()) - this.getElementJQuery()?.on("fileselect", { e, _ -> - this.dispatchEvent("fileSelectUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("fileclear", { e, _ -> - this.dispatchEvent("fileClearUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("filereset", { e, _ -> - this.dispatchEvent("fileResetUpload", obj { detail = e }) - }) - this.getElementJQuery()?.on("filebrowse", { e, _ -> - this.dispatchEvent("fileBrowseUpload", obj { detail = e }) - }) - this.getElementJQueryD()?.on("filepreupload", lambda@{ _, data, previewId, index -> - data["previewId"] = previewId - data["index"] = index - this.dispatchEvent("filePreUpload", obj { detail = data }) - return@lambda null - }) - } - - override fun afterDestroy() { - getElementJQueryD()?.fileinput("destroy") - } - - private fun refreshUploadInput() { - getElementJQueryD()?.fileinput("refresh", getSettingsObj()) - } - - /** - * Resets the file input control. - */ - open fun resetInput() { - getElementJQueryD()?.fileinput("reset") - } - - /** - * Clears the file input control (including the native input). - */ - open fun clearInput() { - getElementJQueryD()?.fileinput("clear") - } - - /** - * Trigger ajax upload (only for ajax mode). - */ - open fun upload() { - getElementJQueryD()?.fileinput("upload") - } - - /** - * Cancel an ongoing ajax upload (only for ajax mode). - */ - open fun cancel() { - getElementJQueryD()?.fileinput("cancel") - } - - /** - * Locks the file input (disabling all buttons except a cancel button). - */ - open fun lock() { - getElementJQueryD()?.fileinput("lock") - } - - /** - * Unlocks the file input. - */ - open fun unlock() { - getElementJQueryD()?.fileinput("unlock") - } - - /** - * Returns the native JavaScript File object. - * @param kFile KFile object - * @return File object - */ - fun getNativeFile(kFile: KFile): File? { - return nativeFiles[kFile] - } - - private fun getFiles(): List<KFile> { - nativeFiles.clear() - return (getElementJQueryD()?.fileinput("getFileStack") as Array<File>).toList().map { - val kFile = KFile(it.name, it.size, null) - nativeFiles[kFile] = it - kFile - } - } - - /** - * Returns the value of the file input control as a String. - * @return value as a String - */ - fun getValueAsString(): String? { - return value?.joinToString { it.name } - } - - /** - * Makes the input element focused. - */ - open fun focus() { - getElementJQuery()?.focus() - } - - /** - * Makes the input element blur. - */ - open fun blur() { - getElementJQuery()?.blur() - } - - private fun getSettingsObj(): dynamic { - val language = I18n.language - return obj { - this.uploadUrl = uploadUrl - this.uploadExtraData = uploadExtraData ?: undefined - this.theme = if (explorerTheme) "explorer-fa" else null - this.required = required - this.showCaption = showCaption - this.showPreview = showPreview - this.showRemove = showRemove - this.showUpload = showUpload - this.showCancel = showCancel - this.showBrowse = showBrowse - this.browseOnZoneClick = browseOnZoneClick - this.preferIconicPreview = preferIconicPreview - this.allowedFileTypes = allowedFileTypes?.toTypedArray() - this.allowedFileExtensions = allowedFileExtensions?.toTypedArray() - this.dropZoneEnabled = dropZoneEnabled - this.language = language - } - } - - companion object { - internal var counter = 0 - - /** - * DSL builder extension function. - * - * It takes the same parameters as the constructor of the built component. - */ - fun Container.uploadInput( - uploadUrl: String? = null, - multiple: Boolean = false, - classes: Set<String> = setOf(), - init: (UploadInput.() -> Unit)? = null - ): UploadInput { - val uploadInput = UploadInput(uploadUrl, multiple, classes).apply { - init?.invoke( - this - ) - } - this.add(uploadInput) - return uploadInput - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt index 99f4ca13..0b6b43e5 100644 --- a/src/main/kotlin/pl/treksoft/kvision/html/Button.kt +++ b/src/main/kotlin/pl/treksoft/kvision/html/Button.kt @@ -32,7 +32,7 @@ import pl.treksoft.kvision.core.Widget /** * Button styles. */ -enum class ButtonStyle(internal val className: String) { +enum class ButtonStyle(val className: String) { DEFAULT("btn-default"), PRIMARY("btn-primary"), SUCCESS("btn-success"), diff --git a/src/main/kotlin/pl/treksoft/kvision/i18n/I18n.kt b/src/main/kotlin/pl/treksoft/kvision/i18n/I18n.kt index 343c18ef..b61d6e40 100644 --- a/src/main/kotlin/pl/treksoft/kvision/i18n/I18n.kt +++ b/src/main/kotlin/pl/treksoft/kvision/i18n/I18n.kt @@ -21,24 +21,15 @@ */ package pl.treksoft.kvision.i18n -import org.w3c.xhr.XMLHttpRequest import pl.treksoft.kvision.panel.Root import kotlin.browser.window -import kotlin.js.Promise - -external class Jed(json: dynamic) { - fun gettext(key: String): String - fun ngettext(singularKey: String, pluralKey: String, value: Int): String - fun sprintf(format: String, value: Int): String -} - -private const val I18N_SINGLE_DELIMITER = "###KvI18nS###" -private const val I18N_PLURAL_DELIMITER = "###KvI18nP###" /** * A singleton object used for translations. */ -object I18n { +object I18n : I18nManager { + + var manager: I18nManager = SimpleI18nManager() private val defaultLanguage = window.navigator.language.split("-")[0] @@ -51,112 +42,12 @@ object I18n { Root.roots.forEach { it.restart() } } - private val cache = mutableMapOf<String, Jed>() - - /** - * I18n initialization function. - * Should be called in the main function of the application. - * @param languages a list of supported languages. - * @param initCallback a code to run after the initialization process is complete. - */ - fun init(vararg languages: String, initCallback: () -> Unit) { - val promises = languages.map { - I18n.readMessages(it) - }.toTypedArray() - Promise.all(promises).then { initCallback() } + override fun gettext(key: String): String { + return manager.gettext(key) } - @Suppress("MagicNumber") - private fun readMessages(language: String): Promise<Jed> { - return Promise { resolve, _ -> - val xmlHttpRequest = XMLHttpRequest() - xmlHttpRequest.overrideMimeType("application/json") - xmlHttpRequest.open("GET", "js/messages-$language.json", true) - xmlHttpRequest.onreadystatechange = { - if (xmlHttpRequest.readyState.toInt() == 4 && (xmlHttpRequest.status.toInt() == 200 || - xmlHttpRequest.status.toInt() == 0) - ) { - val json = JSON.parse<dynamic>(xmlHttpRequest.responseText) - val jed = Jed(json) - cache[language] = jed - resolve(jed) - } - } - xmlHttpRequest.send() - } + override fun ngettext(singularKey: String, pluralKey: String, value: Int): String { + return manager.ngettext(singularKey, pluralKey, value) } - /** - * A static translation function for a singular form. - * @param key a translation key. - * @return translated text. - */ - fun gettext(key: String): String { - return cache[language]?.gettext(key) ?: key - } - - /** - * A static translation function for a plural form. - * @param singularKey a translation key for a singular form. - * @param pluralKey a translation key for a plural form. - * @param value a count value. - * @return translated text. - */ - fun ngettext(singularKey: String, pluralKey: String, value: Int): String { - return cache[language]?.run { - sprintf(ngettext(singularKey, pluralKey, value), value) - } ?: if (value == 1) singularKey else pluralKey - } - - /** - * A dynamic translation function for a singular form. - * @param key a translation key. - * @return text marked for a dynamic translation. - */ - fun tr(key: String): String { - return I18N_SINGLE_DELIMITER + key - } - - /** - * A dynamic translation function for a plural form. - * @param singularKey a translation key for a singular form. - * @param pluralKey a translation key for a plural form. - * @param value a count value. - * @return text marked for a dynamic translation. - */ - fun ntr(singularKey: String, pluralKey: String, value: Int): String { - return I18N_PLURAL_DELIMITER + singularKey + I18N_PLURAL_DELIMITER + pluralKey + I18N_PLURAL_DELIMITER + value - } - - /** - * A dynamic translation function. - * @param text text marked for a dynamic translation. - * @return translated text. - */ - fun trans(text: String): String { - return if (text.startsWith(I18N_SINGLE_DELIMITER)) { - gettext(text.substring(I18N_SINGLE_DELIMITER.length)) - } else if (text.startsWith(I18N_PLURAL_DELIMITER)) { - val tab = text.substring(I18N_PLURAL_DELIMITER.length).split(I18N_PLURAL_DELIMITER) - @Suppress("MagicNumber") - if (tab.size == 3) { - ngettext(tab[0], tab[1], tab[2].toIntOrNull() ?: 1) - } else { - text - } - } else { - text - } - } - - /** - * A dynamic translation function. - * @param text text marked for a dynamic translation. - * @return translated text. - */ - fun trans(text: String?): String? { - return text?.let { - trans(it) - } - } } diff --git a/src/main/kotlin/pl/treksoft/kvision/i18n/I18nManager.kt b/src/main/kotlin/pl/treksoft/kvision/i18n/I18nManager.kt new file mode 100644 index 00000000..09890e6f --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/i18n/I18nManager.kt @@ -0,0 +1,96 @@ +/* + * 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.i18n + +private const val I18N_SINGLE_DELIMITER = "###KvI18nS###" +private const val I18N_PLURAL_DELIMITER = "###KvI18nP###" + +interface I18nManager { + /** + * A static translation function for a singular form. + * @param key a translation key. + * @return translated text. + */ + fun gettext(key: String): String + + /** + * A static translation function for a plural form. + * @param singularKey a translation key for a singular form. + * @param pluralKey a translation key for a plural form. + * @param value a count value. + * @return translated text. + */ + fun ngettext(singularKey: String, pluralKey: String, value: Int): String + + /** + * A dynamic translation function for a singular form. + * @param key a translation key. + * @return text marked for a dynamic translation. + */ + fun tr(key: String): String { + return I18N_SINGLE_DELIMITER + key + } + + /** + * A dynamic translation function for a plural form. + * @param singularKey a translation key for a singular form. + * @param pluralKey a translation key for a plural form. + * @param value a count value. + * @return text marked for a dynamic translation. + */ + fun ntr(singularKey: String, pluralKey: String, value: Int): String { + return I18N_PLURAL_DELIMITER + singularKey + I18N_PLURAL_DELIMITER + pluralKey + I18N_PLURAL_DELIMITER + value + } + + /** + * A dynamic translation function. + * @param text text marked for a dynamic translation. + * @return translated text. + */ + fun trans(text: String): String { + return if (text.startsWith(I18N_SINGLE_DELIMITER)) { + gettext(text.substring(I18N_SINGLE_DELIMITER.length)) + } else if (text.startsWith(I18N_PLURAL_DELIMITER)) { + val tab = text.substring(I18N_PLURAL_DELIMITER.length).split(I18N_PLURAL_DELIMITER) + @Suppress("MagicNumber") + if (tab.size == 3) { + ngettext(tab[0], tab[1], tab[2].toIntOrNull() ?: 1) + } else { + text + } + } else { + text + } + } + + /** + * A dynamic translation function. + * @param text text marked for a dynamic translation. + * @return translated text. + */ + fun trans(text: String?): String? { + return text?.let { + trans(it) + } + } + +} diff --git a/src/main/kotlin/pl/treksoft/kvision/i18n/SimpleI18nManager.kt b/src/main/kotlin/pl/treksoft/kvision/i18n/SimpleI18nManager.kt new file mode 100644 index 00000000..d8d5897c --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/i18n/SimpleI18nManager.kt @@ -0,0 +1,34 @@ +/* + * 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.i18n + +class SimpleI18nManager : I18nManager { + + override fun gettext(key: String): String { + return key + } + + override fun ngettext(singularKey: String, pluralKey: String, value: Int): String { + return singularKey + } + +} diff --git a/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt b/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt index d4d73b29..c81ebde1 100644 --- a/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt +++ b/src/main/kotlin/pl/treksoft/kvision/types/KDate.kt @@ -28,12 +28,12 @@ import kotlin.js.Date @Suppress("MayBeConstant", "TopLevelPropertyNaming") actual val KDATE_FORMAT = "YYYY-MM-DD HH:mm:ss" -internal actual fun nowDate(): KDate = +actual fun nowDate(): KDate = KDate(Date().getTime().toLong()) -internal actual fun String.toKDateF(format: String): KDate = +actual fun String.toKDateF(format: String): KDate = this.toDateF(format)?.getTime()?.toLong()?.let { KDate(it) } ?: KDate() -internal actual fun KDate.toStringF(format: String) = this.toJS().toStringF(format) +actual fun KDate.toStringF(format: String) = this.toJS().toStringF(format) fun KDate.toJS(): kotlin.js.Date = kotlin.js.Date(this.time) |