aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt49
-rw-r--r--kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt31
2 files changed, 71 insertions, 9 deletions
diff --git a/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt b/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt
index 55fcdb34..383e734e 100644
--- a/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt
+++ b/kvision-modules/kvision-redux/src/main/kotlin/pl/treksoft/kvision/redux/StateBinding.kt
@@ -36,30 +36,42 @@ import redux.RAction
* @param container a container
* @param factory a function which re-creates the view based on the given state
*/
-class StateBinding<S : Any, A : RAction, CONT : Container>(
+class StateBinding<S : Any, A : RAction, CONT : Container, CONTENT>(
store: ReduxStore<S, A>,
private val container: CONT,
- private val factory: (CONT.(S) -> Unit)
+ private val factory: (CONT.(S) -> CONTENT)
) : Widget(setOf()) {
init {
- container.add(this)
- container.factory(store.getState())
+ update(store.getState())
store.subscribe { update(it) }
}
+ private var updateState: ((S, CONTENT) -> Unit)? = null
+ private var content: CONTENT? = null
+
/**
* Updates view from the current state.
*/
@Suppress("ComplexMethod")
fun update(state: S) {
singleRender {
- container.removeAll()
- container.add(this)
- container.factory(state)
+ if (updateState == null || content == null) {
+ container.removeAll()
+ container.add(this)
+ content = container.factory(state)
+ } else {
+ content?.let {
+ updateState?.invoke(state, it)
+ }
+ }
}
}
+ private fun setUpdateState(updateState: (S, CONTENT) -> Unit) {
+ this.updateState = updateState
+ }
+
companion object {
/**
* DSL builder extension function.
@@ -69,8 +81,29 @@ class StateBinding<S : Any, A : RAction, CONT : Container>(
fun <S : Any, A : RAction, CONT : Container> CONT.stateBinding(
store: ReduxStore<S, A>,
factory: (CONT.(S) -> Unit)
- ): StateBinding<S, A, CONT> {
+ ): StateBinding<S, A, CONT, Unit> {
return StateBinding(store, this, factory)
}
+
+ /**
+ * DSL builder extension function for updateable redux content.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+ fun <S : Any, A : RAction, CONT : Container, CONTENT> CONT.stateUpdate(
+ store: ReduxStore<S, A>,
+ factory: (CONT.(S) -> CONTENT)
+ ): Updateable<S, CONTENT> {
+ return Updateable(StateBinding(store, this, factory)::setUpdateState)
+ }
+ }
+}
+
+/**
+ * A helper class for updateable redux content.
+ */
+class Updateable<S : Any, CONTENT>(private val setUpdateState: ((S, CONTENT) -> Unit) -> Unit) {
+ infix fun updateWith(updateState: (S, CONTENT) -> Unit) {
+ setUpdateState(updateState)
}
}
diff --git a/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt b/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
index 1da16e55..0271a956 100644
--- a/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
+++ b/kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
@@ -26,6 +26,7 @@ import pl.treksoft.kvision.html.Div.Companion.div
import pl.treksoft.kvision.panel.Root
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.redux.StateBinding.Companion.stateBinding
+import pl.treksoft.kvision.redux.StateBinding.Companion.stateUpdate
import pl.treksoft.kvision.redux.createReduxStore
import redux.RAction
import test.pl.treksoft.kvision.DomSpec
@@ -77,4 +78,32 @@ class StateBindingSpec : DomSpec {
}
}
-} \ No newline at end of file
+ @Test
+ fun stateUpdate() {
+ run {
+ val root = Root("test", true)
+ val store = createReduxStore(::stateReducer, State(10))
+
+ val container = SimplePanel()
+ container.stateUpdate(store) { state ->
+ div("${state.counter}")
+ } updateWith { state, d ->
+ d.content = "${state.counter}"
+ }
+ root.add(container)
+ val element = document.getElementById("test")
+ assertEqualsHtml(
+ "<div><div></div><div>10</div></div>",
+ element?.innerHTML,
+ "Should render initial state of the container"
+ )
+ store.dispatch(StateAction.Inc)
+ assertEqualsHtml(
+ "<div><div></div><div>11</div></div>",
+ element?.innerHTML,
+ "Should render changed state of the container"
+ )
+ }
+ }
+
+}