diff options
author | Robert Jaros <rjaros@finn.pl> | 2017-11-25 13:39:39 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2017-11-25 13:39:39 +0100 |
commit | 343390df5a0e01f45539939291c35d535a1b8af6 (patch) | |
tree | fcba5b87efa1b740bd5a011739af1e066cba28f0 /src/main/kotlin/pl/treksoft/kvision/form | |
parent | 4a31ea44d479358658a614ad56a5675436260813 (diff) | |
download | kvision-343390df5a0e01f45539939291c35d535a1b8af6.tar.gz kvision-343390df5a0e01f45539939291c35d535a1b8af6.tar.bz2 kvision-343390df5a0e01f45539939291c35d535a1b8af6.zip |
Form validation
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision/form')
18 files changed, 384 insertions, 92 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FieldLabel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FieldLabel.kt index 92b014d4..748ace83 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/FieldLabel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/FieldLabel.kt @@ -4,8 +4,9 @@ import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag import pl.treksoft.kvision.snabbdom.StringPair -open class FieldLabel(private val forId: String, text: String? = null, rich: Boolean = false) : Tag(TAG.LABEL, - text, rich) { +open class FieldLabel(private val forId: String, text: String? = null, rich: Boolean = false, + classes: Set<String> = setOf("control-label")) : Tag(TAG.LABEL, + text, rich, classes = classes) { override fun getSnAttrs(): List<StringPair> { return super.getSnAttrs() + ("for" to forId) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt new file mode 100644 index 00000000..66f6f450 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt @@ -0,0 +1,96 @@ +package pl.treksoft.kvision.form + +import kotlin.js.Date + +data class FieldParams<in F : FormControl>(val required: Boolean = false, + val validatorMessage: ((F) -> String?)? = null, + val validator: ((F) -> Boolean?)? = null) + +open class Form<K>(private val panel: FormPanel<K>? = null, private val modelFactory: (Map<String, Any?>) -> K) { + + internal val fields: MutableMap<String, FormControl> = mutableMapOf() + internal val fieldsParams: MutableMap<String, Any> = mutableMapOf() + internal var validatorMessage: ((Form<K>) -> String?)? = null + internal var validator: ((Form<K>) -> Boolean?)? = null + + open fun <C : FormControl> add(key: String, control: C, required: Boolean = false, + validatorMessage: ((C) -> String?)? = null, + validator: ((C) -> Boolean?)? = null): Form<K> { + this.fields.put(key, control) + this.fieldsParams.put(key, FieldParams(required, validatorMessage, validator)) + return this + } + + open fun remove(key: String): Form<K> { + this.fields.remove(key) + return this + } + + open fun removeAll(): Form<K> { + this.fields.clear() + return this + } + + open fun getControl(key: String): FormControl? { + return this.fields[key] + } + + operator fun get(key: String): Any? { + return getControl(key)?.getValue() + } + + open fun setData(data: K) { + fields.forEach { it.value.setValue(null) } + val map = data.asDynamic().map as? Map<String, Any?> + if (map != null) { + map.forEach { + fields[it.key]?.setValue(it.value) + } + } else { + for (key in js("Object").keys(data)) { + @Suppress("UnsafeCastFromDynamic") + fields[key]?.setValue(data.asDynamic()[key]) + } + } + } + + open fun getData(): K { + val map = fields.entries.associateBy({ it.key }, { it.value.getValue() }) + return modelFactory(map) + } + + open fun validate(): Boolean { + val fieldWithError = fieldsParams.mapNotNull { entry -> + fields[entry.key]?.let { control -> + @Suppress("UNCHECKED_CAST") + val fieldsParams = (entry.value as? FieldParams<FormControl>) + val required = fieldsParams?.required ?: false + val requiredError = control.getValue() == null && required + if (requiredError) { + control.validatorError = "Value is required" + true + } else { + val validatorPassed = fieldsParams?.validator?.invoke(control) ?: true + control.validatorError = if (!validatorPassed) { + fieldsParams?.validatorMessage?.invoke(control) ?: "Invalid value" + } else { + null + } + !validatorPassed + } + } + }.find { it } + val validatorPassed = validator?.invoke(this) ?: true + panel?.validatorError = if (!validatorPassed) { + panel?.validatorMessage?.invoke(this) ?: "Invalid form data" + } else { + null + } + return fieldWithError == null && validatorPassed + } +} + +fun Map<String, Any?>.string(key: String): String? = this[key] as? String +fun Map<String, Any?>.number(key: String): Number? = this[key] as? Number +fun Map<String, Any?>.bool(key: String): Boolean? = this[key] as? Boolean +fun Map<String, Any?>.date(key: String): Date? = this[key] as? Date diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt new file mode 100644 index 00000000..d0858d4e --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt @@ -0,0 +1,68 @@ +package pl.treksoft.kvision.form + +import pl.treksoft.kvision.core.Component +import kotlin.js.Date + +enum class INPUTSIZE(val className: String) { + LARGE("input-lg"), + SMALL("input-sm") +} + +interface FormControl : Component { + var disabled: Boolean + var size: INPUTSIZE? + val input: Component + val flabel: FieldLabel + val validationInfo: HelpBlock + fun getValue(): Any? + fun setValue(v: Any?) + fun getValueAsString(): String? + fun refresh(): Component + var validatorError: String? + get() = validationInfo.text + set(value) { + validationInfo.text = value + validationInfo.visible = value != null + refresh() + } +} + +interface StringFormControl : FormControl { + var value: String? + override fun getValue(): String? = value + override fun setValue(v: Any?) { + value = v as? String + } + + override fun getValueAsString(): String? = value +} + +interface NumberFormControl : FormControl { + var value: Number? + override fun getValue(): Number? = value + override fun setValue(v: Any?) { + value = v as? Number + } + + override fun getValueAsString(): String? = value?.toString() +} + +interface BoolFormControl : FormControl { + var value: Boolean + override fun getValue(): Boolean = value + override fun setValue(v: Any?) { + value = v as? Boolean ?: false + } + + override fun getValueAsString(): String? = value.toString() +} + +interface DateFormControl : FormControl { + var value: Date? + override fun getValue(): Date? = value + override fun setValue(v: Any?) { + value = v as? Date + } + + override fun getValueAsString(): String? = value?.toString() +} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormField.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormField.kt deleted file mode 100644 index 503ee51d..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/FormField.kt +++ /dev/null @@ -1,34 +0,0 @@ -package pl.treksoft.kvision.form - -import kotlin.js.Date - -enum class INPUTSIZE(val className: String) { - LARGE("input-lg"), - SMALL("input-sm") -} - -interface FormField { - var disabled: Boolean - var size: INPUTSIZE? - fun getValueAsString(): String? -} - -interface StringFormField : FormField { - var value: String? - override fun getValueAsString(): String? = value -} - -interface NumberFormField : FormField { - var value: Number? - override fun getValueAsString(): String? = value?.toString() -} - -interface BoolFormField : FormField { - var value: Boolean - override fun getValueAsString(): String? = value.toString() -} - -interface DateFormField : FormField { - var value: Date? - override fun getValueAsString(): String? = value?.toString() -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt new file mode 100644 index 00000000..e996cfae --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt @@ -0,0 +1,106 @@ +package pl.treksoft.kvision.form + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.form.check.CheckBox +import pl.treksoft.kvision.form.check.Radio +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.snabbdom.StringBoolPair + +enum class FORMTYPE(val formType: String) { + INLINE("form-inline"), + HORIZONTAL("form-horizontal") +} + +open class FormPanel<K>(private val type: FORMTYPE? = null, classes: Set<String> = setOf(), + modelFactory: (Map<String, Any?>) -> K) : SimplePanel(classes) { + + var validatorMessage + get() = form.validatorMessage + set(value) { + form.validatorMessage = value + } + var validator + get() = form.validator + set(value) { + form.validator = value + } + + internal var validatorError: String? + get() = validationAlert.text + set(value) { + validationAlert.text = value + validationAlert.visible = value != null + refresh() + } + + @Suppress("LeakingThis") + protected val form = Form(this, modelFactory) + protected val validationAlert = Tag(TAG.H5, classes = setOf("alert", "alert-danger")).apply { + role = "alert" + visible = false + } + + init { + this.addInternal(validationAlert) + } + + override fun render(): VNode { + return kvh("form", childrenVNodes()) + } + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + if (type != null) { + cl.add(type.formType to true) + } + return cl + } + + open fun <C : FormControl> add(key: String, control: C, required: Boolean = false, + validatorMessage: ((C) -> String?)? = null, + validator: ((C) -> Boolean?)? = null): FormPanel<K> { + if (type == FORMTYPE.HORIZONTAL) { + if (control is CheckBox || control is Radio) { + control.addCssClass("col-sm-offset-2") + control.addCssClass("col-sm-10") + } else { + control.flabel.addCssClass("col-sm-2") + control.input.addSurroundingCssClass("col-sm-10") + control.validationInfo.addCssClass("col-sm-offset-2") + control.validationInfo.addCssClass("col-sm-10") + } + } + super.add(control) + form.add(key, control, required, validatorMessage, validator) + return this + } + + fun remove(key: String): FormPanel<K> { + form.getControl(key)?.let { + super.remove(it) + } + form.remove(key) + return this + } + + override fun removeAll(): FormPanel<K> { + super.removeAll() + this.addInternal(validationAlert) + form.removeAll() + return this + } + + open fun setData(data: K) { + form.setData(data) + } + + open fun getData(): K { + return form.getData() + } + + open fun validate(): Boolean { + return form.validate() + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt b/src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt new file mode 100644 index 00000000..44b2e7b0 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt @@ -0,0 +1,7 @@ +package pl.treksoft.kvision.form + +import pl.treksoft.kvision.html.TAG +import pl.treksoft.kvision.html.Tag + +open class HelpBlock(text: String? = null, rich: Boolean = false) : Tag(TAG.SPAN, text, rich, + classes = setOf("help-block", "small")) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt index e4dd7fe9..62403f8a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt @@ -1,8 +1,9 @@ package pl.treksoft.kvision.form.check import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.form.BoolFormField +import pl.treksoft.kvision.form.BoolFormControl import pl.treksoft.kvision.form.FieldLabel +import pl.treksoft.kvision.form.HelpBlock import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.SnOn import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -17,7 +18,7 @@ enum class CHECKBOXSTYLE(val className: String) { } open class CheckBox(value: Boolean = false, label: String? = null, - rich: Boolean = false) : SimplePanel(setOf("checkbox")), BoolFormField { + rich: Boolean = false) : SimplePanel(setOf("checkbox")), BoolFormControl { override var value get() = input.value @@ -71,14 +72,17 @@ open class CheckBox(value: Boolean = false, label: String? = null, } private val idc = "kv_form_checkbox_" + counter - val input: CheckInput = CheckInput(CHECKINPUTTYPE.CHECKBOX, value, setOf("styled")).apply { id = idc } - val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val input: CheckInput = CheckInput(CHECKINPUTTYPE.CHECKBOX, value, + setOf("styled")).apply { id = idc } + final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf()) + final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { @Suppress("LeakingThis") input.eventTarget = this this.addInternal(input) this.addInternal(flabel) + this.addInternal(validationInfo) counter++ } @@ -113,6 +117,9 @@ open class CheckBox(value: Boolean = false, label: String? = null, if (inline) { cl.add("checkbox-inline" to true) } + if (validatorError != null) { + cl.add("has-error" to true) + } return cl } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt index a4899f45..a2ebd06d 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt @@ -2,7 +2,6 @@ package pl.treksoft.kvision.form.check import com.github.snabbdom.VNode import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.form.BoolFormField import pl.treksoft.kvision.form.INPUTSIZE import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair @@ -13,7 +12,7 @@ enum class CHECKINPUTTYPE(val type: String) { } open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, value: Boolean = false, - classes: Set<String> = setOf()) : Widget(classes), BoolFormField { + classes: Set<String> = setOf()) : Widget(classes) { init { this.setInternalEventListener<CheckInput> { @@ -27,12 +26,12 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, value: Boo } } } - override var value: Boolean = value + + var value: Boolean = value set(value) { field = value refreshState() } - @Suppress("LeakingThis") var startValue: Boolean = value set(value) { field = value @@ -49,7 +48,7 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, value: Boo field = value refresh() } - override var disabled: Boolean = false + var disabled: Boolean = false set(value) { field = value refresh() @@ -59,7 +58,7 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, value: Boo field = value refresh() } - override var size: INPUTSIZE? = null + var size: INPUTSIZE? = null set(value) { field = value refresh() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt index 284cf26c..1a28870a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt @@ -1,8 +1,9 @@ package pl.treksoft.kvision.form.check import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.form.BoolFormField +import pl.treksoft.kvision.form.BoolFormControl import pl.treksoft.kvision.form.FieldLabel +import pl.treksoft.kvision.form.HelpBlock import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.SnOn import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -17,7 +18,7 @@ enum class RADIOSTYLE(val className: String) { } open class Radio(value: Boolean = false, extraValue: String? = null, label: String? = null, - rich: Boolean = false) : SimplePanel(), BoolFormField { + rich: Boolean = false) : SimplePanel(), BoolFormControl { override var value get() = input.value @@ -76,17 +77,19 @@ open class Radio(value: Boolean = false, extraValue: String? = null, label: Stri } private val idc = "kv_form_radio_" + counter - val input: CheckInput = CheckInput(CHECKINPUTTYPE.RADIO, value).apply { + final override val input: CheckInput = CheckInput(CHECKINPUTTYPE.RADIO, value).apply { this.id = idc this.extraValue = extraValue } - val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val flabel: FieldLabel = FieldLabel(idc, label, rich, classes = setOf()) + final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { @Suppress("LeakingThis") input.eventTarget = this this.addInternal(input) this.addInternal(flabel) + this.addInternal(validationInfo) counter++ } @@ -129,6 +132,9 @@ open class Radio(value: Boolean = false, extraValue: String? = null, label: Stri cl.add("checkbox-inline" to true) } } + if (validatorError != null) { + cl.add("has-error" to true) + } return cl } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt index 4b991605..2d750683 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt @@ -1,16 +1,19 @@ package pl.treksoft.kvision.form.select +import pl.treksoft.kvision.core.Component import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.form.FieldLabel -import pl.treksoft.kvision.form.StringFormField +import pl.treksoft.kvision.form.HelpBlock +import pl.treksoft.kvision.form.StringFormControl import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.SnOn +import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair @Suppress("TooManyFunctions") open class Select(options: List<StringPair>? = null, value: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, - rich: Boolean = false) : SimplePanel(setOf("form-group")), StringFormField { + rich: Boolean = false) : SimplePanel(setOf("form-group")), StringFormControl { var options get() = input.options @@ -99,15 +102,17 @@ open class Select(options: List<StringPair>? = null, value: String? = null, } private val idc = "kv_form_select_" + counter - val input: SelectInput = SelectInput(options, value, multiple, ajaxOptions, + final override val input: SelectInput = SelectInput(options, value, multiple, ajaxOptions, setOf("form-control")).apply { id = idc } - val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { - this.addInternal(flabel) @Suppress("LeakingThis") input.eventTarget = this + this.addInternal(flabel) this.addInternal(input) + this.addInternal(validationInfo) counter++ } @@ -115,6 +120,14 @@ open class Select(options: List<StringPair>? = null, value: String? = null, var counter = 0 } + 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) @@ -131,17 +144,17 @@ open class Select(options: List<StringPair>? = null, value: String? = null, return this } - override fun add(child: Widget): SimplePanel { + override fun add(child: Component): SimplePanel { input.add(child) return this } - override fun addAll(children: List<Widget>): SimplePanel { + override fun addAll(children: List<Component>): SimplePanel { input.addAll(children) return this } - override fun remove(child: Widget): SimplePanel { + override fun remove(child: Component): SimplePanel { input.remove(child) return this } @@ -151,7 +164,7 @@ open class Select(options: List<StringPair>? = null, value: String? = null, return this } - override fun getChildren(): List<Widget> { + override fun getChildren(): List<Component> { return input.getChildren() } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt index c95cf434..2dcbe99b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -3,9 +3,8 @@ package pl.treksoft.kvision.form.select import com.github.snabbdom.VNode import pl.treksoft.kvision.core.CssSize import pl.treksoft.kvision.core.KVManager.KVNULL -import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.core.Component import pl.treksoft.kvision.form.INPUTSIZE -import pl.treksoft.kvision.form.StringFormField import pl.treksoft.kvision.html.BUTTONSTYLE import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.StringBoolPair @@ -20,7 +19,7 @@ enum class SELECTWIDTHTYPE(val value: String) { @Suppress("TooManyFunctions") open class SelectInput(options: List<StringPair>? = null, value: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, - classes: Set<String> = setOf()) : SimplePanel(classes), StringFormField { + classes: Set<String> = setOf()) : SimplePanel(classes) { internal var options = options set(value) { @@ -28,14 +27,12 @@ open class SelectInput(options: List<StringPair>? = null, value: String? = null, setChildrenFromOptions() } - @Suppress("LeakingThis") - override var value: String? = value + var value: String? = value set(value) { field = value refreshState() } - @Suppress("LeakingThis") var startValue: String? = value set(value) { field = value @@ -93,7 +90,7 @@ open class SelectInput(options: List<StringPair>? = null, value: String? = null, field = value setChildrenFromOptions() } - override var disabled: Boolean = false + var disabled: Boolean = false set(value) { field = value refresh() @@ -103,7 +100,7 @@ open class SelectInput(options: List<StringPair>? = null, value: String? = null, field = value refresh() } - override var size: INPUTSIZE? = null + var size: INPUTSIZE? = null set(value) { field = value refresh() @@ -140,19 +137,19 @@ open class SelectInput(options: List<StringPair>? = null, value: String? = null, return kvh("select", childrenVNodes()) } - override fun add(child: Widget): SimplePanel { + override fun add(child: Component): SimplePanel { super.add(child) refreshSelectInput() return this } - override fun addAll(children: List<Widget>): SimplePanel { + override fun addAll(children: List<Component>): SimplePanel { super.addAll(children) refreshSelectInput() return this } - override fun remove(child: Widget): SimplePanel { + override fun remove(child: Component): SimplePanel { super.remove(child) refreshSelectInput() return this diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt index 0b50cbab..8dd2a241 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt @@ -2,12 +2,14 @@ package pl.treksoft.kvision.form.text import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.form.FieldLabel -import pl.treksoft.kvision.form.StringFormField +import pl.treksoft.kvision.form.HelpBlock +import pl.treksoft.kvision.form.StringFormControl import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.SnOn +import pl.treksoft.kvision.snabbdom.StringBoolPair abstract class AbstractText(label: String? = null, rich: Boolean = false) : - SimplePanel(setOf("form-group")), StringFormField { + SimplePanel(setOf("form-group")), StringFormControl { override var value get() = input.value @@ -66,8 +68,9 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) : } protected val idc = "kv_form_text_" + counter - internal abstract val input: AbstractTextInput - internal val flabel: FieldLabel = FieldLabel(idc, label, rich) + abstract override val input: AbstractTextInput + final override val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { this.addInternal(flabel) @@ -78,6 +81,14 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) : var counter = 0 } + 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) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt index a71a84a5..0303f5bd 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt @@ -2,12 +2,11 @@ package pl.treksoft.kvision.form.text import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.form.INPUTSIZE -import pl.treksoft.kvision.form.StringFormField import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair abstract class AbstractTextInput(value: String? = null, - classes: Set<String> = setOf()) : Widget(classes), StringFormField { + classes: Set<String> = setOf()) : Widget(classes) { init { this.setInternalEventListener<AbstractTextInput> { @@ -17,12 +16,11 @@ abstract class AbstractTextInput(value: String? = null, } } - override var value: String? = value + var value: String? = value set(value) { field = value refreshState() } - @Suppress("LeakingThis") var startValue: String? = value set(value) { field = value @@ -44,7 +42,7 @@ abstract class AbstractTextInput(value: String? = null, field = value refresh() } - override var disabled: Boolean = false + var disabled: Boolean = false set(value) { field = value refresh() @@ -59,7 +57,7 @@ abstract class AbstractTextInput(value: String? = null, field = value refresh() } - override var size: INPUTSIZE? = null + var size: INPUTSIZE? = null set(value) { field = value refresh() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt index b963b4ea..280c017d 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt @@ -15,5 +15,6 @@ open class RichText(value: String? = null, @Suppress("LeakingThis") input.eventTarget = this this.addInternal(input) + this.addInternal(validationInfo) } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt index d5d78616..db3233ba 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt @@ -21,5 +21,6 @@ open class Text(type: TEXTINPUTTYPE = TEXTINPUTTYPE.TEXT, value: String? = null, @Suppress("LeakingThis") input.eventTarget = this this.addInternal(input) + this.addInternal(validationInfo) } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt index ecfe9cee..181298ac 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt @@ -25,5 +25,6 @@ open class TextArea(cols: Int? = null, rows: Int? = null, value: String? = null, @Suppress("LeakingThis") input.eventTarget = this this.addInternal(input) + this.addInternal(validationInfo) } } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt index ae732030..892467b7 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt @@ -1,14 +1,16 @@ package pl.treksoft.kvision.form.time import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.form.DateFormField +import pl.treksoft.kvision.form.DateFormControl import pl.treksoft.kvision.form.FieldLabel +import pl.treksoft.kvision.form.HelpBlock import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.SnOn +import pl.treksoft.kvision.snabbdom.StringBoolPair import kotlin.js.Date open class DateTime(value: Date? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null, - rich: Boolean = false) : SimplePanel(setOf("form-group")), DateFormField { + rich: Boolean = false) : SimplePanel(setOf("form-group")), DateFormControl { override var value get() = input.value @@ -97,14 +99,16 @@ open class DateTime(value: Date? = null, format: String = "YYYY-MM-DD HH:mm", la } protected val idc = "kv_form_time_" + counter - internal val input: DateTimeInput = DateTimeInput(value, format).apply { id = idc } - internal val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val input: DateTimeInput = DateTimeInput(value, format).apply { id = idc } + final override val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val validationInfo: HelpBlock = HelpBlock().apply { visible = false } init { - this.addInternal(flabel) @Suppress("LeakingThis") input.eventTarget = this + this.addInternal(flabel) this.addInternal(input) + this.addInternal(validationInfo) counter++ } @@ -112,6 +116,14 @@ open class DateTime(value: Date? = null, format: String = "YYYY-MM-DD HH:mm", la var counter = 0 } + 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) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt index 78321c10..8db66834 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt @@ -2,7 +2,6 @@ package pl.treksoft.kvision.form.time import com.github.snabbdom.VNode import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.form.DateFormField import pl.treksoft.kvision.form.INPUTSIZE import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair @@ -16,7 +15,7 @@ const val MAX_VIEW = 4 @Suppress("TooManyFunctions") open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm", - classes: Set<String> = setOf()) : Widget(classes + "form-control"), DateFormField { + classes: Set<String> = setOf()) : Widget(classes + "form-control") { init { @@ -27,7 +26,7 @@ open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm } } - override var value: Date? = value + var value: Date? = value set(value) { field = value refreshState() @@ -47,7 +46,7 @@ open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm field = value refresh() } - override var disabled: Boolean = false + var disabled: Boolean = false set(value) { field = value refresh() @@ -62,7 +61,7 @@ open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm field = value refresh() } - override var size: INPUTSIZE? = null + var size: INPUTSIZE? = null set(value) { field = value refresh() @@ -147,7 +146,10 @@ open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm protected open fun refreshState() { value?.let { getElementJQueryD()?.datetimepicker("update", it) - } ?: getElementJQueryD()?.datetimepicker("update", null) + } ?: run { + getElementJQueryD()?.`val`(null) + getElementJQueryD()?.datetimepicker("update", null) + } } protected open fun refreshDatePicker() { @@ -212,7 +214,7 @@ open class DateTimeInput(value: Date? = null, format: String = "YYYY-MM-DD HH:mm }) } - override fun getValueAsString(): String? { + fun getValueAsString(): String? { return value?.toStringF(format) } |