aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2020-05-20 17:12:56 +0200
committerRobert Jaros <rjaros@finn.pl>2020-05-20 17:12:56 +0200
commitef1abc3b975e1c77fc8663c5e5e95769fc934551 (patch)
tree343236378e2f24514635fb5e892043a054c86170
parent35628c364d9768a5589559b99cb698ebc2b56eba (diff)
downloadkvision-ef1abc3b975e1c77fc8663c5e5e95769fc934551.tar.gz
kvision-ef1abc3b975e1c77fc8663c5e5e95769fc934551.tar.bz2
kvision-ef1abc3b975e1c77fc8663c5e5e95769fc934551.zip
Make all stateful components implement ObservableState interface
-rw-r--r--kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt9
-rw-r--r--kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt11
-rw-r--r--kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt9
-rw-r--r--kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt9
-rw-r--r--kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt17
-rw-r--r--kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt9
-rw-r--r--kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt17
-rw-r--r--kvision-modules/kvision-react/src/main/kotlin/pl/treksoft/kvision/react/React.kt16
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt2
-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.kt24
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt9
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt19
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt17
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/range/Range.kt9
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt17
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt9
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt17
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt10
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt26
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt2
21 files changed, 244 insertions, 29 deletions
diff --git a/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt b/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt
index 68ec96a2..115781cd 100644
--- a/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt
+++ b/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTime.kt
@@ -28,6 +28,7 @@ import pl.treksoft.kvision.form.DateFormControl
import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
import kotlin.js.Date
@@ -44,7 +45,7 @@ import kotlin.js.Date
open class DateTime(
value: Date? = null, name: String? = null, format: String = "YYYY-MM-DD HH:mm", label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-group")), DateFormControl {
+) : SimplePanel(setOf("form-group")), DateFormControl, ObservableState<Date?> {
/**
* Date/time input value.
@@ -264,6 +265,12 @@ open class DateTime(
input.blur()
}
+ override fun getState(): Date? = input.getState()
+
+ override fun subscribe(observer: (Date?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt b/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt
index 967ca9db..d870947b 100644
--- a/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt
+++ b/kvision-modules/kvision-bootstrap-datetime/src/main/kotlin/pl/treksoft/kvision/form/time/DateTimeInput.kt
@@ -33,6 +33,7 @@ import pl.treksoft.kvision.html.icon
import pl.treksoft.kvision.html.span
import pl.treksoft.kvision.i18n.I18n
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.types.toDateF
import pl.treksoft.kvision.types.toStringF
import pl.treksoft.kvision.utils.obj
@@ -53,7 +54,7 @@ internal const val DEFAULT_STEPPING = 5
open class DateTimeInput(
value: Date? = null, format: String = "YYYY-MM-DD HH:mm",
classes: Set<String> = setOf()
-) : SimplePanel(classes + "input-group" + "date"), FormInput {
+) : SimplePanel(classes + "input-group" + "date"), FormInput, ObservableState<Date?> {
private var initialized = false
@@ -404,6 +405,14 @@ open class DateTimeInput(
override fun blur() {
input.blur()
}
+
+ override fun getState(): Date? = input.getState()?.toDateF(format)
+
+ override fun subscribe(observer: (Date?) -> Unit): () -> Unit {
+ return input.subscribe { str ->
+ observer(str?.toDateF(format))
+ }
+ }
}
/**
diff --git a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt
index b4e4738c..18c522d5 100644
--- a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt
+++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt
@@ -31,6 +31,7 @@ import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.remote.KVServiceManager
import pl.treksoft.kvision.remote.RemoteOption
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -60,7 +61,7 @@ open class SelectRemote<T : Any>(
preload: Boolean = false,
label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-group")), StringFormControl {
+) : SimplePanel(setOf("form-group")), StringFormControl, ObservableState<String?> {
/**
* A value of the selected option.
*/
@@ -254,6 +255,12 @@ open class SelectRemote<T : Any>(
input.blur()
}
+ override fun getState(): String? = input.getState()
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt
index 4ffece7d..5fb92ab8 100644
--- a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt
+++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -52,7 +53,7 @@ open class Select(
options: List<StringPair>? = null, value: String? = null, name: String? = null,
multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-group")), StringFormControl {
+) : SimplePanel(setOf("form-group")), StringFormControl, ObservableState<String?> {
/**
* A list of options (value to label pairs) for the select control.
@@ -272,6 +273,12 @@ open class Select(
input.blur()
}
+ override fun getState(): String? = input.getState()
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt
index ced0945d..5cba791c 100644
--- a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt
+++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt
@@ -33,6 +33,7 @@ import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.html.ButtonStyle
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.asString
import pl.treksoft.kvision.utils.obj
import pl.treksoft.kvision.utils.set
@@ -72,7 +73,9 @@ open class SelectInput(
options: List<StringPair>? = null, value: String? = null,
multiple: Boolean = false, ajaxOptions: AjaxOptions? = null,
classes: Set<String> = setOf()
-) : SimplePanel(classes), FormInput {
+) : SimplePanel(classes), FormInput, ObservableState<String?> {
+
+ protected val observers = mutableListOf<(String?) -> Unit>()
/**
* A list of options (value to label pairs) for the select control.
@@ -82,7 +85,7 @@ open class SelectInput(
/**
* A value of the selected option.
*/
- var value by refreshOnUpdate(value) { refreshState() }
+ var value by refreshOnUpdate(value) { refreshState(); observers.forEach { ob -> ob(it) } }
/**
* The name attribute of the generated HTML select element.
@@ -396,6 +399,16 @@ open class SelectInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): String? = value
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
/**
diff --git a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt
index 3901cc4f..2dbcd443 100644
--- a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt
+++ b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/Spinner.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.NumberFormControl
import pl.treksoft.kvision.html.ButtonStyle
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -52,7 +53,7 @@ open class Spinner(
decimals: Int = 0, val buttonsType: ButtonsType = ButtonsType.VERTICAL,
forceType: ForceType = ForceType.NONE, buttonStyle: ButtonStyle? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-group")), NumberFormControl {
+) : SimplePanel(setOf("form-group")), NumberFormControl, ObservableState<Number?> {
/**
* Spinner value.
@@ -252,6 +253,12 @@ open class Spinner(
invalidFeedback.addCssClass("col-sm-${horizontalRatio.fields}")
}
+ override fun getState(): Number? = input.getState()
+
+ override fun subscribe(observer: (Number?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
index 98b7be62..9ed9d707 100644
--- a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
+++ b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt
@@ -31,6 +31,7 @@ import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.html.ButtonStyle
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.obj
import pl.treksoft.kvision.utils.set
@@ -75,12 +76,14 @@ open class SpinnerInput(
decimals: Int = 0, val buttonsType: ButtonsType = ButtonsType.VERTICAL,
forceType: ForceType = ForceType.NONE, buttonStyle: ButtonStyle? = null,
classes: Set<String> = setOf()
-) : Widget(classes + "form-control"), FormInput {
+) : Widget(classes + "form-control"), FormInput, ObservableState<Number?> {
+
+ protected val observers = mutableListOf<(Number?) -> Unit>()
/**
* Spinner value.
*/
- var value by refreshOnUpdate(value) { refreshState() }
+ var value by refreshOnUpdate(value) { refreshState(); observers.forEach { ob -> ob(it) } }
/**
* The value attribute of the generated HTML input element.
@@ -328,6 +331,16 @@ open class SpinnerInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): Number? = value
+
+ override fun subscribe(observer: (Number?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
/**
diff --git a/kvision-modules/kvision-react/src/main/kotlin/pl/treksoft/kvision/react/React.kt b/kvision-modules/kvision-react/src/main/kotlin/pl/treksoft/kvision/react/React.kt
index 7424159f..79f4d1a0 100644
--- a/kvision-modules/kvision-react/src/main/kotlin/pl/treksoft/kvision/react/React.kt
+++ b/kvision-modules/kvision-react/src/main/kotlin/pl/treksoft/kvision/react/React.kt
@@ -27,6 +27,7 @@ import org.w3c.dom.HTMLElement
import pl.treksoft.kvision.KVManagerReact
import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.set
import react.RBuilder
import react.dom.render as ReactRender
@@ -41,12 +42,15 @@ class React<S>(
state: S,
classes: Set<String> = setOf(),
private val builder: RBuilder.(getState: () -> S, changeState: ((S) -> S) -> Unit) -> Unit
-) : Widget(classes) {
+) : Widget(classes), ObservableState<S> {
+
+ private val observers = mutableListOf<(S) -> Unit>()
var state = state
set(value) {
field = value
refreshFunction?.invoke()
+ observers.forEach { it(state) }
}
private var refreshFunction: (() -> Unit)? = null
@@ -76,6 +80,16 @@ class React<S>(
KVManagerReact.reactDom.unmountComponentAtNode(it)
}
}
+
+ override fun getState(): S = state
+
+ override fun subscribe(observer: (S) -> Unit): () -> Unit {
+ observers += observer
+ observer(state)
+ return {
+ observers -= observer
+ }
+ }
}
/**
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index ba4db0cc..b28943dd 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -1017,7 +1017,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
* An extension function which binds the widget to the observable state.
* Used by [pl.treksoft.kvision.state.bind]
*/
- internal fun <S : Any, W : Widget> W.bindState(
+ internal fun <S, W : Widget> W.bindState(
observableState: ObservableState<S>,
factory: (W.(S) -> Unit)
): W {
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 60147114..370f5ab1 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.FormHorizontalRatio
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -55,7 +56,7 @@ enum class CheckBoxStyle(internal val className: String) {
open class CheckBox(
value: Boolean = false, name: String? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-check", "abc-checkbox")), BoolFormControl {
+) : SimplePanel(setOf("form-check", "abc-checkbox")), BoolFormControl, ObservableState<Boolean> {
/**
* The selection state of the checkbox.
@@ -65,6 +66,7 @@ open class CheckBox(
set(value) {
input.value = value
}
+
/**
* The value attribute of the generated HTML input element.
*
@@ -76,6 +78,7 @@ open class CheckBox(
set(value) {
input.startValue = value
}
+
/**
* The label text bound to the input element.
*/
@@ -84,6 +87,7 @@ open class CheckBox(
set(value) {
flabel.content = value
}
+
/**
* Determines if [label] can contain HTML code.
*/
@@ -92,14 +96,17 @@ open class CheckBox(
set(value) {
flabel.rich = value
}
+
/**
* The style (one of Bootstrap standard colors) of the input.
*/
var style: CheckBoxStyle? by refreshOnUpdate()
+
/**
* Determines if the checkbox is rendered as a circle.
*/
var circled by refreshOnUpdate(false)
+
/**
* Determines if the checkbox is rendered inline.
*/
@@ -189,6 +196,12 @@ open class CheckBox(
addCssClass("form-group")
}
+ override fun getState(): Boolean = input.getState()
+
+ override fun subscribe(observer: (Boolean) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
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 f93fd436..70d57c51 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt
@@ -29,6 +29,7 @@ import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
+import pl.treksoft.kvision.state.ObservableState
/**
* Type of the check input control (checkbox or radio).
@@ -49,7 +50,9 @@ enum class CheckInputType(internal val type: String) {
abstract class CheckInput(
type: CheckInputType = CheckInputType.CHECKBOX, value: Boolean = false,
classes: Set<String> = setOf()
-) : Widget(classes), FormInput {
+) : Widget(classes), FormInput, ObservableState<Boolean> {
+
+ protected val observers = mutableListOf<(Boolean) -> Unit>()
init {
this.setInternalEventListener<CheckInput> {
@@ -67,7 +70,8 @@ abstract class CheckInput(
/**
* The selection state of the input.
*/
- var value by refreshOnUpdate(value) { refreshState() }
+ var value by refreshOnUpdate(value) { refreshState(); observers.forEach { ob -> ob(it) } }
+
/**
* The value attribute of the generated HTML input element.
*
@@ -75,26 +79,32 @@ abstract class CheckInput(
* bound to the input selection state.
*/
var startValue by refreshOnUpdate(value) { this.value = it; refresh() }
+
/**
* The type of the generated HTML input element.
*/
var type by refreshOnUpdate(type)
+
/**
* The name attribute of the generated HTML input element.
*/
override var name: String? by refreshOnUpdate()
+
/**
* Determines if the field is disabled.
*/
override var disabled by refreshOnUpdate(false)
+
/**
* The additional String value used for the radio button group.
*/
var extraValue: String? by refreshOnUpdate()
+
/**
* The size of the input.
*/
override var size: InputSize? by refreshOnUpdate()
+
/**
* The validation status of the input.
*/
@@ -169,4 +179,14 @@ abstract class CheckInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): Boolean = value
+
+ override fun subscribe(observer: (Boolean) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
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 b8757da2..03ae8595 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.FormHorizontalRatio
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -56,7 +57,7 @@ enum class RadioStyle(internal val className: String) {
open class Radio(
value: Boolean = false, extraValue: String? = null, name: String? = null, label: String? = null,
rich: Boolean = false
-) : SimplePanel(classes = setOf("form-check")), BoolFormControl {
+) : SimplePanel(classes = setOf("form-check")), BoolFormControl, ObservableState<Boolean> {
/**
* The selection state of the radio button.
@@ -204,6 +205,12 @@ open class Radio(
addCssClass("form-group")
}
+ override fun getState(): Boolean = input.getState()
+
+ override fun subscribe(observer: (Boolean) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
index 234f5327..6433d708 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt
@@ -31,6 +31,7 @@ import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
/**
* The form field component rendered as a group of HTML *input type="radio"* elements with the same name attribute.
@@ -51,7 +52,9 @@ open class RadioGroup(
options: List<StringPair>? = null, value: String? = null, name: String? = null, inline: Boolean = false,
label: String? = null,
rich: Boolean = false
-) : SimplePanel(setOf("form-group")), StringFormControl {
+) : SimplePanel(setOf("form-group")), StringFormControl, ObservableState<String?> {
+
+ protected val observers = mutableListOf<(String?) -> Unit>()
/**
* A list of options (label to value pairs) for the group.
@@ -61,7 +64,7 @@ open class RadioGroup(
/**
* A value of the selected option.
*/
- override var value by refreshOnUpdate(value) { setValueToChildren(it) }
+ override var value by refreshOnUpdate(value) { setValueToChildren(it); observers.forEach { ob -> ob(it) } }
/**
* Determines if the options are rendered inline.
@@ -73,6 +76,7 @@ open class RadioGroup(
set(value) {
setDisabledToChildren(value)
}
+
/**
* The label text of the options group.
*/
@@ -81,6 +85,7 @@ open class RadioGroup(
set(value) {
flabel.content = value
}
+
/**
* Determines if [label] can contain HTML code.
*/
@@ -226,6 +231,16 @@ open class RadioGroup(
invalidFeedback.addCssClass("col-sm-${horizontalRatio.fields}")
}
+ override fun getState(): String? = value
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
+
companion object {
internal var counter = 0
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
index 5a47c3ce..3e9f0def 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroupInput.kt
@@ -28,6 +28,7 @@ import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
/**
* The input component rendered as a group of HTML *input type="radio"* elements with the same name attribute.
@@ -44,7 +45,9 @@ import pl.treksoft.kvision.panel.SimplePanel
@Suppress("TooManyFunctions")
open class RadioGroupInput(
options: List<StringPair>? = null, value: String? = null, name: String? = null, inline: Boolean = false
-) : SimplePanel(setOf("form-group")), FormInput {
+) : SimplePanel(setOf("form-group")), FormInput, ObservableState<String?> {
+
+ protected val observers = mutableListOf<(String?) -> Unit>()
/**
* A list of options (label to value pairs) for the group.
@@ -54,7 +57,7 @@ open class RadioGroupInput(
/**
* A value of the selected option.
*/
- var value by refreshOnUpdate(value) { setValueToChildren(it) }
+ var value by refreshOnUpdate(value) { setValueToChildren(it); observers.forEach { ob -> ob(it) } }
/**
* Determines if the options are rendered inline.
@@ -172,6 +175,16 @@ open class RadioGroupInput(
getChildren().filterIsInstance<Radio>().firstOrNull()?.blur()
}
+ override fun getState(): String? = value
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
+
companion object {
internal var counter = 0
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/range/Range.kt b/src/main/kotlin/pl/treksoft/kvision/form/range/Range.kt
index dc8e57a0..d5acae8b 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/range/Range.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/range/Range.kt
@@ -29,6 +29,7 @@ import pl.treksoft.kvision.form.FormHorizontalRatio
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.NumberFormControl
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -46,7 +47,7 @@ import pl.treksoft.kvision.utils.SnOn
open class Range(
value: Number? = null, name: String? = null, min: Number = 0, max: Number = 100, step: Number = DEFAULT_STEP,
label: String? = null, rich: Boolean = false
-) : SimplePanel(setOf("form-group")), NumberFormControl {
+) : SimplePanel(setOf("form-group")), NumberFormControl, ObservableState<Number?> {
/**
* Range input value.
@@ -212,6 +213,12 @@ open class Range(
invalidFeedback.addCssClass("col-sm-${horizontalRatio.fields}")
}
+ override fun getState(): Number? = input.getState()
+
+ override fun subscribe(observer: (Number?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt
index 4649c654..6112022d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.set
internal const val DEFAULT_STEP = 1
@@ -47,12 +48,14 @@ internal const val DEFAULT_STEP = 1
open class RangeInput(
value: Number? = null, min: Number = 0, max: Number = 100, step: Number = DEFAULT_STEP,
classes: Set<String> = setOf()
-) : Widget(classes + "form-control-range"), FormInput {
+) : Widget(classes + "form-control-range"), FormInput, ObservableState<Number?> {
+
+ protected val observers = mutableListOf<(Number?) -> Unit>()
/**
* Range input value.
*/
- var value by refreshOnUpdate(value ?: (min as Number?)) { refreshState() }
+ var value by refreshOnUpdate(value ?: (min as Number?)) { refreshState(); observers.forEach { ob -> ob(it) } }
/**
* The value attribute of the generated HTML input element.
@@ -223,6 +226,16 @@ open class RangeInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): Number? = value
+
+ override fun subscribe(observer: (Number?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
/**
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
index e797e050..043e8a02 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelect.kt
@@ -30,6 +30,7 @@ import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -47,7 +48,7 @@ import pl.treksoft.kvision.utils.SnOn
open class SimpleSelect(
options: List<StringPair>? = null, value: String? = null, emptyOption: Boolean = false,
name: String? = null, label: String? = null, rich: Boolean = false
-) : SimplePanel(setOf("form-group")), StringFormControl {
+) : SimplePanel(setOf("form-group")), StringFormControl, ObservableState<String?> {
/**
* A list of options (value to label pairs) for the select control.
@@ -183,6 +184,12 @@ open class SimpleSelect(
input.blur()
}
+ override fun getState(): String? = input.getState()
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
companion object {
internal var counter = 0
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
index 63eb98b5..dd567cad 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt
@@ -31,6 +31,7 @@ import pl.treksoft.kvision.form.ValidationStatus
import pl.treksoft.kvision.html.TAG
import pl.treksoft.kvision.html.Tag
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.set
internal const val KVNULL = "#kvnull"
@@ -47,7 +48,9 @@ internal const val KVNULL = "#kvnull"
open class SimpleSelectInput(
options: List<StringPair>? = null, value: String? = null, emptyOption: Boolean = false,
classes: Set<String> = setOf()
-) : SimplePanel(classes + "form-control"), FormInput {
+) : SimplePanel(classes + "form-control"), FormInput, ObservableState<String?> {
+
+ protected val observers = mutableListOf<(String?) -> Unit>()
/**
* A list of options (value to label pairs) for the select control.
@@ -57,7 +60,7 @@ open class SimpleSelectInput(
/**
* Text input value.
*/
- var value by refreshOnUpdate(value) { refreshState() }
+ var value by refreshOnUpdate(value) { refreshState(); observers.forEach { ob -> ob(it) } }
/**
* The value of the selected child option.
@@ -207,6 +210,16 @@ open class SimpleSelectInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): String? = value
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
/**
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 8a5acf8b..9ac2a636 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractText.kt
@@ -27,6 +27,7 @@ import pl.treksoft.kvision.form.FieldLabel
import pl.treksoft.kvision.form.InvalidFeedback
import pl.treksoft.kvision.form.StringFormControl
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
/**
@@ -37,7 +38,7 @@ import pl.treksoft.kvision.utils.SnOn
* @param rich determines if [label] can contain HTML code
*/
abstract class AbstractText(label: String? = null, rich: Boolean = false) :
- SimplePanel(setOf("form-group")), StringFormControl {
+ SimplePanel(setOf("form-group")), StringFormControl, ObservableState<String?> {
/**
* Text input value.
@@ -155,4 +156,11 @@ abstract class AbstractText(label: String? = null, rich: Boolean = false) :
override fun blur() {
input.blur()
}
+
+ override fun getState(): String? = input.getState()
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ return input.subscribe(observer)
+ }
+
}
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 192b33c8..98bd5f1a 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt
@@ -28,6 +28,7 @@ import pl.treksoft.kvision.core.Widget
import pl.treksoft.kvision.form.FormInput
import pl.treksoft.kvision.form.InputSize
import pl.treksoft.kvision.form.ValidationStatus
+import pl.treksoft.kvision.state.ObservableState
/**
* Base class for basic text components.
@@ -39,7 +40,9 @@ import pl.treksoft.kvision.form.ValidationStatus
abstract class AbstractTextInput(
value: String? = null,
classes: Set<String> = setOf()
-) : Widget(classes), FormInput {
+) : Widget(classes), FormInput, ObservableState<String?> {
+
+ protected val observers = mutableListOf<(String?) -> Unit>()
init {
this.setInternalEventListener<AbstractTextInput> {
@@ -52,7 +55,8 @@ abstract class AbstractTextInput(
/**
* Text input value.
*/
- var value by refreshOnUpdate(value) { refreshState() }
+ var value by refreshOnUpdate(value) { refreshState(); observers.forEach { ob -> ob(it) } }
+
/**
* The value attribute of the generated HTML input element.
*
@@ -60,34 +64,42 @@ abstract class AbstractTextInput(
* bound to the text input value.
*/
var startValue by refreshOnUpdate(value) { this.value = it; refresh() }
+
/**
* The placeholder for the text input.
*/
var placeholder: String? by refreshOnUpdate()
+
/**
* The name attribute of the generated HTML input element.
*/
override var name: String? by refreshOnUpdate()
+
/**
* Maximal length of the text input value.
*/
var maxlength: Int? by refreshOnUpdate()
+
/**
* Determines if the field is disabled.
*/
override var disabled by refreshOnUpdate(false)
+
/**
* Determines if the text input is automatically focused.
*/
var autofocus: Boolean? by refreshOnUpdate()
+
/**
* Determines if the text input is read-only.
*/
var readonly: Boolean? by refreshOnUpdate()
+
/**
* The size of the input.
*/
override var size: InputSize? by refreshOnUpdate()
+
/**
* The validation status of the input.
*/
@@ -174,4 +186,14 @@ abstract class AbstractTextInput(
override fun blur() {
getElementJQuery()?.blur()
}
+
+ override fun getState(): String? = value
+
+ override fun subscribe(observer: (String?) -> Unit): () -> Unit {
+ observers += observer
+ observer(value)
+ return {
+ observers -= observer
+ }
+ }
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt b/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
index d330f76d..6c8090b7 100644
--- a/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
@@ -15,7 +15,7 @@ import pl.treksoft.kvision.core.Widget.Companion.bindState
* @param observableState the state
* @param factory a function which re-creates the view based on the given state
*/
-fun <S : Any, W : Widget> W.bind(
+fun <S, W : Widget> W.bind(
observableState: ObservableState<S>,
factory: (W.(S) -> Unit)
): W {