summaryrefslogtreecommitdiff
path: root/src/main/kotlin/pl/treksoft/kvision
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2017-11-25 13:39:39 +0100
committerRobert Jaros <rjaros@finn.pl>2017-11-25 13:39:39 +0100
commit343390df5a0e01f45539939291c35d535a1b8af6 (patch)
treefcba5b87efa1b740bd5a011739af1e066cba28f0 /src/main/kotlin/pl/treksoft/kvision
parent4a31ea44d479358658a614ad56a5675436260813 (diff)
downloadkvision-343390df5a0e01f45539939291c35d535a1b8af6.tar.gz
kvision-343390df5a0e01f45539939291c35d535a1b8af6.tar.bz2
kvision-343390df5a0e01f45539939291c35d535a1b8af6.zip
Form validation
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision')
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/Showcase.kt89
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Component.kt26
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Container.kt10
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/KVObject.kt3
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt45
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/WidgetWrapper.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt9
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt10
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FieldLabel.kt5
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/Form.kt96
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt68
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormField.kt34
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt106
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/HelpBlock.kt7
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt15
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt11
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt14
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt31
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt19
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt19
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt10
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/RichText.kt1
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/Text.kt1
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/TextArea.kt1
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt22
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt16
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt5
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt20
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt12
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt13
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt8
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt10
35 files changed, 598 insertions, 172 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
index 8e7fcb3b..82651b71 100644
--- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
@@ -7,14 +7,21 @@ import pl.treksoft.kvision.data.DataComponent
import pl.treksoft.kvision.data.DataContainer
import pl.treksoft.kvision.dropdown.DD.*
import pl.treksoft.kvision.dropdown.DropDown
+import pl.treksoft.kvision.form.Form
+import pl.treksoft.kvision.form.FormPanel
import pl.treksoft.kvision.form.INPUTSIZE
+import pl.treksoft.kvision.form.bool
import pl.treksoft.kvision.form.check.CheckBox
+import pl.treksoft.kvision.form.check.Radio
+import pl.treksoft.kvision.form.date
import pl.treksoft.kvision.form.select.AjaxOptions
import pl.treksoft.kvision.form.select.SELECTWIDTHTYPE
import pl.treksoft.kvision.form.select.Select
import pl.treksoft.kvision.form.select.SelectInput
import pl.treksoft.kvision.form.select.SelectOptGroup
import pl.treksoft.kvision.form.select.SelectOption
+import pl.treksoft.kvision.form.string
+import pl.treksoft.kvision.form.text.Password
import pl.treksoft.kvision.form.text.RichText
import pl.treksoft.kvision.form.text.TEXTINPUTTYPE
import pl.treksoft.kvision.form.text.Text
@@ -40,6 +47,35 @@ import kotlin.js.Date
class Showcase : ApplicationBase() {
override fun start(state: Map<String, Any>) {
+ data class DataForm(val a: String?, val b: Boolean?, val c: Date?)
+
+ val f = DataForm("ala", true, Date())
+ val form = Form {
+ DataForm(it.string("a"), it.bool("b"), it.date("c"))
+ }
+ form.add("a", Text())
+ form.add("b", CheckBox())
+ form.add("c", DateTime())
+ form.setData(f)
+ val ret = form.getData()
+ console.log(ret)
+
+ class DataFormMap(val map: Map<String, Any?>) {
+ val name: String by map
+ val age: Date by map
+ }
+
+ val fm = DataFormMap(mapOf("name" to "Ala", "age" to Date()))
+ val formm = Form {
+ DataFormMap(it)
+ }
+ formm.add("name", Text())
+ formm.add("age", DateTime())
+ formm.setData(fm)
+ val retm = formm.getData()
+ console.log(retm.name)
+ console.log(retm.age)
+
val root = Root("showcase")
class Model(p: Boolean, t: String) : DataComponent() {
@@ -286,6 +322,7 @@ class Showcase : ApplicationBase() {
date.value = "2017-01-16".toDateF("YYYY-MM-DD")
date.showPopup()
date.weekStart = 1
+ date4.value = null
date4.format = "mm:HH"
date4.disabled = !date4.disabled
}
@@ -368,6 +405,58 @@ class Showcase : ApplicationBase() {
}
}
+ class Formularz(val map: Map<String, Any?>) {
+ val text: String? by map
+ val password: String? by map
+ val textarea: String? by map
+ val richtext: String? by map
+ val data: Date? by map
+ val checkbox: Boolean by map
+ val radio: Boolean by map
+ val select: String? by map
+ }
+
+ val formPanel = FormPanel() {
+ Formularz(it)
+ }.apply {
+ add("text", Text(label = "Tekst"), required = true, validatorMessage = { "Wprowadź tylko cyfry" }) {
+ it.getValue()?.matches("^[0-9]+$")
+ }
+ add("password", Password(label = "Hasło"), required = true,
+ validatorMessage = { "Wprowadź co najmniej 5 znaków" }) {
+ (it.getValue()?.length ?: 0) >= 5
+ }
+ add("textarea", TextArea(label = "Obszar"), required = true)
+ add("richtext", RichText(label = "Obszar WYSIWYG"), required = true)
+ add("data", DateTime(format = "YYYY-MM-DD", label = "Data"), required = true)
+ add("checkbox", CheckBox(label = "Checkbox")) { it.getValue() }
+ add("radio", Radio(label = "Radiobutton")) { it.getValue() }
+ add("select", Select(options = listOf("a" to "Pierwsza opcja", "b" to "Druga opcja"),
+ label = "Wybierz opcje").apply {
+ // selectWidthType = SELECTWIDTHTYPE.FIT
+ emptyOption = true
+ }, required = true)
+
+ validator = {
+ var result = it["text"] == it["textarea"]
+ if (!result) {
+ it.getControl("text")?.validatorError = "Niezgodne dane"
+ it.getControl("textarea")?.validatorError = "Niezgodne dane"
+ }
+ result
+ }
+ validatorMessage = { "Pole Tekst i Obszar muszą być takie same!" }
+ }
+ root.add(formPanel)
+ val formButton = Button("Pokaż dane").setEventListener<Button> {
+ click = {
+ console.log(formPanel.validate())
+ console.log(formPanel.getData().map.toString())
+// formPanel.setData(Formularz(mapOf("zazn" to false, "select" to "a")))
+ }
+ }
+ formPanel.add(formButton)
+
val dd = DropDown("Dropdown", listOf("abc" to "#!/x", "def" to "#!/y"), "flag")
root.add(dd)
dd.setEventListener<DropDown> {
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Component.kt b/src/main/kotlin/pl/treksoft/kvision/core/Component.kt
new file mode 100644
index 00000000..af5ac830
--- /dev/null
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Component.kt
@@ -0,0 +1,26 @@
+package pl.treksoft.kvision.core
+
+import com.github.snabbdom.VNode
+import org.w3c.dom.Node
+import pl.treksoft.jquery.JQuery
+
+@Suppress("TooManyFunctions")
+interface Component {
+ var parent: Component?
+ var visible: Boolean
+ var width: CssSize?
+ var height: CssSize?
+
+ fun addCssClass(css: String): Widget
+ fun removeCssClass(css: String): Widget
+ fun addSurroundingCssClass(css: String): Widget
+ fun removeSurroundingCssClass(css: String): Widget
+
+ fun renderVNode(): VNode
+ fun getElement(): Node?
+ fun getElementJQuery(): JQuery?
+ fun getElementJQueryD(): dynamic
+ fun clearParent(): Component
+ fun getRoot(): Root?
+ fun dispose()
+}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Container.kt b/src/main/kotlin/pl/treksoft/kvision/core/Container.kt
index 4621a050..da436aba 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Container.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Container.kt
@@ -3,12 +3,12 @@ package pl.treksoft.kvision.core
import com.github.snabbdom.VNode
interface Container {
- var parent: Widget?
+ var parent: Component?
var visible: Boolean
fun renderVNode(): VNode
- fun add(child: Widget): Container
- fun addAll(children: List<Widget>): Container
- fun remove(child: Widget): Container
+ fun add(child: Component): Container
+ fun addAll(children: List<Component>): Container
+ fun remove(child: Component): Container
fun removeAll(): Container
- fun getChildren(): List<Widget>
+ fun getChildren(): List<Component>
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/KVObject.kt b/src/main/kotlin/pl/treksoft/kvision/core/KVObject.kt
deleted file mode 100644
index aa90ccf8..00000000
--- a/src/main/kotlin/pl/treksoft/kvision/core/KVObject.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package pl.treksoft.kvision.core
-
-interface KVObject
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
index 8b883d82..12ed797e 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt
@@ -2,9 +2,9 @@ package pl.treksoft.kvision.core
import pl.treksoft.kvision.snabbdom.StringPair
-abstract class StyledComponent : KVObject {
+abstract class StyledComponent : Component {
- var width: CssSize? = null
+ override var width: CssSize? = null
set(value) {
field = value
refresh()
@@ -19,7 +19,7 @@ abstract class StyledComponent : KVObject {
field = value
refresh()
}
- var height: CssSize? = null
+ override var height: CssSize? = null
set(value) {
field = value
refresh()
@@ -138,7 +138,7 @@ abstract class StyledComponent : KVObject {
private var snStyleCache: List<StringPair>? = null
- protected open fun refresh(): StyledComponent {
+ open fun refresh(): StyledComponent {
snStyleCache = null
return this
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index 46aa2a90..fec62ec7 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -21,13 +21,13 @@ import pl.treksoft.kvision.snabbdom.snStyle
open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
internal val classes = classes.toMutableSet()
+ internal val surroundingClasses: MutableSet<String> = mutableSetOf()
internal val internalListeners = mutableListOf<SnOn<Widget>.() -> Unit>()
internal val listeners = mutableListOf<SnOn<Widget>.() -> Unit>()
- var parent: Widget? = null
- internal set
+ override var parent: Component? = null
- open var visible: Boolean = true
+ override var visible: Boolean = true
set(value) {
val oldField = field
field = value
@@ -65,8 +65,15 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
return t
}
- open fun renderVNode(): VNode {
- return render()
+ override fun renderVNode(): VNode {
+ return if (surroundingClasses.isEmpty()) {
+ render()
+ } else {
+ val opt = snOpt {
+ `class` = snClasses(surroundingClasses.map { c -> c to true })
+ }
+ h("div", opt, arrayOf(render()))
+ }
}
protected open fun render(): VNode {
@@ -258,31 +265,43 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
return this
}
- open fun addCssClass(css: String): Widget {
+ override fun addCssClass(css: String): Widget {
this.classes.add(css)
refresh()
return this
}
- open fun removeCssClass(css: String): Widget {
+ override fun removeCssClass(css: String): Widget {
this.classes.remove(css)
refresh()
return this
}
- open fun getElement(): Node? {
+ override fun addSurroundingCssClass(css: String): Widget {
+ this.surroundingClasses.add(css)
+ refresh()
+ return this
+ }
+
+ override fun removeSurroundingCssClass(css: String): Widget {
+ this.surroundingClasses.remove(css)
+ refresh()
+ return this
+ }
+
+ override fun getElement(): Node? {
return this.vnode?.elm
}
- open fun getElementJQuery(): JQuery? {
+ override fun getElementJQuery(): JQuery? {
return getElement()?.let { jQuery(it) }
}
- open fun getElementJQueryD(): dynamic {
+ override fun getElementJQueryD(): dynamic {
return getElement()?.let { jQuery(it).asDynamic() }
}
- internal fun clearParent(): Widget {
+ override fun clearParent(): Widget {
this.parent = null
return this
}
@@ -309,7 +328,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
protected open fun afterDestroy() {
}
- internal open fun getRoot(): Root? {
+ override fun getRoot(): Root? {
return this.parent?.getRoot()
}
@@ -333,6 +352,6 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
return this.getElement()?.dispatchEvent(event)
}
- open fun dispose() {
+ override fun dispose() {
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/WidgetWrapper.kt b/src/main/kotlin/pl/treksoft/kvision/core/WidgetWrapper.kt
index 6f6c2f57..26f9308d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/WidgetWrapper.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/WidgetWrapper.kt
@@ -2,7 +2,7 @@ package pl.treksoft.kvision.core
import com.github.snabbdom.VNode
-open class WidgetWrapper(internal var delegate: Widget?, classes: Set<String> = setOf()) : Widget(classes) {
+open class WidgetWrapper(internal var delegate: Component?, classes: Set<String> = setOf()) : Widget(classes) {
override var visible
get() = delegate?.visible == true
diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt
index 5af2eb66..019ee414 100644
--- a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt
@@ -3,6 +3,7 @@ package pl.treksoft.kvision.data
import com.github.snabbdom.VNode
import com.lightningkite.kotlin.observable.list.ObservableList
import pl.treksoft.kvision.core.Container
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.panel.VPanel
@@ -25,17 +26,17 @@ class DataContainer<M : DataComponent, C : Widget>(val model: ObservableList<M>,
update()
}
- override fun add(child: Widget): Container {
+ override fun add(child: Component): Container {
this.child.add(child)
return this
}
- override fun addAll(children: List<Widget>): Container {
+ override fun addAll(children: List<Component>): Container {
this.child.addAll(children)
return this
}
- override fun remove(child: Widget): Container {
+ override fun remove(child: Component): Container {
this.child.remove(child)
return this
}
@@ -45,7 +46,7 @@ class DataContainer<M : DataComponent, C : Widget>(val model: ObservableList<M>,
return this
}
- override fun getChildren(): List<Widget> {
+ override fun getChildren(): List<Component> {
return this.child.getChildren()
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
index 3f6ca828..880cb436 100644
--- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt
@@ -1,7 +1,7 @@
package pl.treksoft.kvision.dropdown
import com.github.snabbdom.VNode
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.html.BUTTONSTYLE
import pl.treksoft.kvision.html.Button
import pl.treksoft.kvision.html.LIST
@@ -87,17 +87,17 @@ open class DropDown(text: String, elements: List<StringPair>? = null, icon: Stri
var counter = 0
}
- override fun add(child: Widget): SimplePanel {
+ override fun add(child: Component): SimplePanel {
list.add(child)
return this
}
- override fun addAll(children: List<Widget>): SimplePanel {
+ override fun addAll(children: List<Component>): SimplePanel {
list.addAll(children)
return this
}
- override fun remove(child: Widget): SimplePanel {
+ override fun remove(child: Component): SimplePanel {
list.remove(child)
return this
}
@@ -107,7 +107,7 @@ open class DropDown(text: String, elements: List<StringPair>? = null, icon: Stri
return this
}
- override fun getChildren(): List<Widget> {
+ override fun getChildren(): List<Component> {
return list.getChildren()
}
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)
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
index 77e40349..08d56f4c 100644
--- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt
@@ -1,6 +1,7 @@
package pl.treksoft.kvision.modal
import com.github.snabbdom.VNode
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.Root
import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.helpers.CloseIcon
@@ -87,12 +88,12 @@ open class Modal(caption: String? = null, closeButton: Boolean = true,
}
}
- override fun add(child: Widget): SimplePanel {
+ override fun add(child: Component): SimplePanel {
body.add(child)
return this
}
- override fun addAll(children: List<Widget>): SimplePanel {
+ override fun addAll(children: List<Component>): SimplePanel {
body.addAll(children)
return this
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt
index 6e037c28..376e34b9 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/DockPanel.kt
@@ -1,6 +1,6 @@
package pl.treksoft.kvision.panel
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
enum class SIDE {
LEFT,
@@ -11,11 +11,11 @@ enum class SIDE {
}
open class DockPanel(classes: Set<String> = setOf()) : SimplePanel(classes = classes) {
- protected var left: Widget? = null
- protected var center: Widget? = null
- protected var right: Widget? = null
- protected var up: Widget? = null
- protected var down: Widget? = null
+ protected var left: Component? = null
+ protected var center: Component? = null
+ protected var right: Component? = null
+ protected var up: Component? = null
+ protected var down: Component? = null
protected val mainContainer = FlexPanel(direction = FLEXDIR.COLUMN, justify = FLEXJUSTIFY.SPACEBETWEEN,
alignItems = FLEXALIGNITEMS.STRETCH)
@@ -27,7 +27,7 @@ open class DockPanel(classes: Set<String> = setOf()) : SimplePanel(classes = cla
}
@Suppress("MagicNumber")
- open fun add(widget: Widget, position: SIDE): DockPanel {
+ open fun add(widget: Component, position: SIDE): DockPanel {
when (position) {
SIDE.UP -> {
up?.let { mainContainer.remove(it) }
@@ -58,16 +58,16 @@ open class DockPanel(classes: Set<String> = setOf()) : SimplePanel(classes = cla
return this
}
- override fun add(child: Widget): DockPanel {
+ override fun add(child: Component): DockPanel {
return this.add(child, SIDE.CENTER)
}
- override fun addAll(children: List<Widget>): DockPanel {
+ override fun addAll(children: List<Component>): DockPanel {
children.forEach { this.add(it) }
return this
}
- override fun remove(child: Widget): DockPanel {
+ override fun remove(child: Component): DockPanel {
if (child == left) removeAt(SIDE.LEFT)
if (child == center) removeAt(SIDE.CENTER)
if (child == right) removeAt(SIDE.RIGHT)
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
index 6b6e11fb..80eddb23 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/FlexPanel.kt
@@ -1,6 +1,6 @@
package pl.treksoft.kvision.panel
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.WidgetWrapper
import pl.treksoft.kvision.snabbdom.StringPair
@@ -73,22 +73,22 @@ open class FlexPanel(direction: FLEXDIR? = null, wrap: FLEXWRAP? = null, justify
}
@Suppress("LongParameterList")
- fun add(child: Widget, order: Int? = null, grow: Int? = null, shrink: Int? = null,
+ fun add(child: Component, order: Int? = null, grow: Int? = null, shrink: Int? = null,
basis: Int? = null, alignSelf: FLEXALIGNITEMS? = null, classes: Set<String> = setOf()): FlexPanel {
addInternal(FlexWrapper(child, order, grow, shrink, basis, alignSelf, classes))
return this
}
- override fun add(child: Widget): FlexPanel {
+ override fun add(child: Component): FlexPanel {
return add(child, null)
}
- override fun addAll(children: List<Widget>): FlexPanel {
+ override fun addAll(children: List<Component>): FlexPanel {
children.forEach { add(it, null) }
return this
}
- override fun remove(child: Widget): FlexPanel {
+ override fun remove(child: Component): FlexPanel {
children.find { (it as FlexWrapper).delegate == child }?.let {
super.remove(it)
it.dispose()
@@ -128,7 +128,7 @@ open class FlexPanel(direction: FLEXDIR? = null, wrap: FLEXWRAP? = null, justify
}
}
-class FlexWrapper(delegate: Widget, private val order: Int? = null, private val grow: Int? = null,
+class FlexWrapper(delegate: Component, private val order: Int? = null, private val grow: Int? = null,
private val shrink: Int? = null, private val basis: Int? = null,
private val alignSelf: FLEXALIGNITEMS? = null,
classes: Set<String> = setOf()) : WidgetWrapper(delegate, classes) {
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
index cac5c79b..dac5867c 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/GridPanel.kt
@@ -1,6 +1,6 @@
package pl.treksoft.kvision.panel
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.WidgetWrapper
import pl.treksoft.kvision.snabbdom.StringPair
@@ -112,23 +112,23 @@ open class GridPanel(autoColumns: String? = null, autoRows: String? = null, auto
}
@Suppress("LongParameterList")
- fun add(child: Widget, columnStart: Int? = null, rowStart: Int? = null,
+ fun add(child: Component, columnStart: Int? = null, rowStart: Int? = null,
columnEnd: String? = null, rowEnd: String? = null, area: String? = null, justifySelf: GRIDJUSTIFY? = null,
alignSelf: GRIDALIGN? = null, classes: Set<String> = setOf()): GridPanel {
addInternal(GridWrapper(child, columnStart, rowStart, columnEnd, rowEnd, area, justifySelf, alignSelf, classes))
return this
}
- override fun add(child: Widget): GridPanel {
+ override fun add(child: Component): GridPanel {
return add(child, null, null)
}
- override fun addAll(children: List<Widget>): GridPanel {
+ override fun addAll(children: List<Component>): GridPanel {
children.forEach { add(it, null, null) }
return this
}
- override fun remove(child: Widget): GridPanel {
+ override fun remove(child: Component): GridPanel {
children.find { (it as GridWrapper).delegate == child }?.let {
super.remove(it)
it.dispose()
@@ -190,7 +190,7 @@ open class GridPanel(autoColumns: String? = null, autoRows: String? = null, auto
}
}
-class GridWrapper(delegate: Widget, private val columnStart: Int? = null, private val rowStart: Int? = null,
+class GridWrapper(delegate: Component, private val columnStart: Int? = null, private val rowStart: Int? = null,
private val columnEnd: String? = null, private val rowEnd: String? = null,
private val area: String? = null, private val justifySelf: GRIDJUSTIFY? = null,
private val alignSelf: GRIDALIGN? = null,
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
index 3587ea8e..88d70a64 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/ResponsiveGridPanel.kt
@@ -1,6 +1,6 @@
package pl.treksoft.kvision.panel
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.WidgetWrapper
import pl.treksoft.kvision.html.ALIGN
import pl.treksoft.kvision.html.TAG
@@ -15,7 +15,7 @@ enum class GRIDSIZE(val size: String) {
const val MAX_COLUMNS = 12
-internal data class WidgetParam(val widget: Widget, val size: Int, val offset: Int)
+internal data class WidgetParam(val widget: Component, val size: Int, val offset: Int)
open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD,
private var rows: Int = 0, private var cols: Int = 0, align: ALIGN? = null,
@@ -29,7 +29,7 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD,
internal val map = mutableMapOf<Int, MutableMap<Int, WidgetParam>>()
private var auto: Boolean = true
- open fun add(child: Widget, row: Int, col: Int, size: Int = 0, offset: Int = 0): ResponsiveGridPanel {
+ open fun add(child: Component, row: Int, col: Int, size: Int = 0, offset: Int = 0): ResponsiveGridPanel {
val cRow = if (row < 0) 0 else row
val cCol = if (col < 0) 0 else col
if (row > rows - 1) rows = cRow + 1
@@ -40,17 +40,17 @@ open class ResponsiveGridPanel(private val gridsize: GRIDSIZE = GRIDSIZE.MD,
return this
}
- override fun add(child: Widget): ResponsiveGridPanel {
+ override fun add(child: Component): ResponsiveGridPanel {
return this.add(child, 0, this.cols)
}
- override fun addAll(children: List<Widget>): ResponsiveGridPanel {
+ override fun addAll(children: List<Component>): ResponsiveGridPanel {
children.forEach { this.add(it) }
return this
}
@Suppress("NestedBlockDepth")
- override fun remove(child: Widget): ResponsiveGridPanel {
+ override fun remove(child: Component): ResponsiveGridPanel {
for (i in 0 until rows) {
val row = map[i]
if (row != null) {
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
index e9cf503b..90362127 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/SimplePanel.kt
@@ -2,10 +2,11 @@ package pl.treksoft.kvision.panel
import com.github.snabbdom.VNode
import pl.treksoft.kvision.core.Container
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.Widget
open class SimplePanel(classes: Set<String> = setOf()) : Widget(classes), Container {
- internal val children: MutableList<Widget> = mutableListOf()
+ internal val children: MutableList<Component> = mutableListOf()
override fun render(): VNode {
return kvh("div", childrenVNodes())
@@ -15,25 +16,25 @@ open class SimplePanel(classes: Set<String> = setOf()) : Widget(classes), Contai
return children.filter { it.visible }.map { it.renderVNode() }.toTypedArray()
}
- protected fun addInternal(child: Widget): SimplePanel {
+ protected fun addInternal(child: Component): SimplePanel {
children.add(child)
child.parent = this
refresh()
return this
}
- override fun add(child: Widget): SimplePanel {
+ override fun add(child: Component): SimplePanel {
return addInternal(child)
}
- override fun addAll(children: List<Widget>): SimplePanel {
+ override fun addAll(children: List<Component>): SimplePanel {
this.children.addAll(children)
children.map { it.parent = this }
refresh()
return this
}
- override fun remove(child: Widget): SimplePanel {
+ override fun remove(child: Component): SimplePanel {
if (children.remove(child)) {
child.clearParent()
refresh()
@@ -48,7 +49,7 @@ open class SimplePanel(classes: Set<String> = setOf()) : Widget(classes), Contai
return this
}
- override fun getChildren(): List<Widget> {
+ override fun getChildren(): List<Component> {
return ArrayList(children)
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
index e8975dce..55bd2cca 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/StackPanel.kt
@@ -1,7 +1,7 @@
package pl.treksoft.kvision.panel
import com.github.snabbdom.VNode
-import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Component
open class StackPanel(private val activateLast: Boolean = true,
classes: Set<String> = setOf()) : SimplePanel(classes) {
@@ -19,21 +19,21 @@ open class StackPanel(private val activateLast: Boolean = true,
}
}
- override fun add(child: Widget): StackPanel {
+ override fun add(child: Component): StackPanel {
super.add(child)
if (activateLast) activeIndex = children.size - 1
else if (activeIndex == -1) activeIndex = 0
return this
}
- override fun addAll(children: List<Widget>): StackPanel {
+ override fun addAll(children: List<Component>): StackPanel {
super.addAll(children)
if (activateLast) activeIndex = this.children.size - 1
else if (activeIndex == -1) activeIndex = 0
return this
}
- override fun remove(child: Widget): StackPanel {
+ override fun remove(child: Component): StackPanel {
super.remove(child)
if (activeIndex > children.size - 1) activeIndex = children.size - 1
return this
diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt
index 4190dbae..e60d9480 100644
--- a/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/panel/TabPanel.kt
@@ -1,7 +1,7 @@
package pl.treksoft.kvision.panel
+import pl.treksoft.kvision.core.Component
import pl.treksoft.kvision.core.ResString
-import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.html.Link
import pl.treksoft.kvision.html.TAG
import pl.treksoft.kvision.html.Tag
@@ -25,7 +25,7 @@ open class TabPanel : SimplePanel(setOf()) {
this.addInternal(content)
}
- open fun addTab(title: String, panel: Widget, icon: String? = null,
+ open fun addTab(title: String, panel: Component, icon: String? = null,
image: ResString? = null): TabPanel {
val tag = Tag(TAG.LI)
tag.role = "presentation"
@@ -53,16 +53,16 @@ open class TabPanel : SimplePanel(setOf()) {
return this
}
- override fun add(child: Widget): TabPanel {
+ override fun add(child: Component): TabPanel {
return addTab("", child)
}
- override fun addAll(children: List<Widget>): TabPanel {
+ override fun addAll(children: List<Component>): TabPanel {
children.forEach { add(it) }
return this
}
- override fun remove(child: Widget): TabPanel {
+ override fun remove(child: Component): TabPanel {
val index = content.children.indexOf(child)
return removeTab(index)
}