aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2020-05-03 22:43:53 +0200
committerRobert Jaros <rjaros@finn.pl>2020-05-03 22:43:53 +0200
commit0b349de3cc699b54b02d7177418c8cf155afe929 (patch)
tree3b01196a3e629e1b7437f7c7e5c8eb413a4ed937
parentd502659176e11f4b1d2294e536d9e3b66b72080c (diff)
downloadkvision-0b349de3cc699b54b02d7177418c8cf155afe929.tar.gz
kvision-0b349de3cc699b54b02d7177418c8cf155afe929.tar.bz2
kvision-0b349de3cc699b54b02d7177418c8cf155afe929.zip
Simplify state binding functions. Deprecate stateBinding() in favor of bind().
-rw-r--r--kvision-modules/kvision-redux-kotlin/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt37
-rw-r--r--kvision-modules/kvision-redux/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt38
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt37
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt25
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/state/StateBindingSpec.kt31
5 files changed, 101 insertions, 67 deletions
diff --git a/kvision-modules/kvision-redux-kotlin/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt b/kvision-modules/kvision-redux-kotlin/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
index ad90770f..cdb8821a 100644
--- a/kvision-modules/kvision-redux-kotlin/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
+++ b/kvision-modules/kvision-redux-kotlin/src/test/kotlin/test/pl/treksoft/kvision/redux/StateBindingSpec.kt
@@ -26,8 +26,8 @@ import pl.treksoft.kvision.panel.Root
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.redux.RAction
import pl.treksoft.kvision.redux.createReduxStore
+import pl.treksoft.kvision.state.bind
import pl.treksoft.kvision.state.stateBinding
-import pl.treksoft.kvision.state.stateUpdate
import test.pl.treksoft.kvision.DomSpec
import kotlin.browser.document
import kotlin.test.Test
@@ -57,51 +57,22 @@ class StateBindingSpec : DomSpec {
val store = createReduxStore(::stateReducer, State(10))
val container = SimplePanel()
- container.stateBinding(store) { state ->
+ container.bind(store) { state ->
div("${state.counter}")
}
root.add(container)
val element = document.getElementById("test")
assertEqualsHtml(
- "<div><div></div><div>10</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>",
+ "<div><div>11</div></div>",
element?.innerHTML,
"Should render changed state of the container"
)
}
}
-
- @Test
- fun stateUpdate() {
- run {
- val root = Root("test", fixed = 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"
- )
- }
- }
-
}
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 dea28ade..b4415fcb 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
@@ -25,8 +25,7 @@ import pl.treksoft.kvision.html.div
import pl.treksoft.kvision.panel.Root
import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.redux.createReduxStore
-import pl.treksoft.kvision.state.stateBinding
-import pl.treksoft.kvision.state.stateUpdate
+import pl.treksoft.kvision.state.bind
import redux.RAction
import test.pl.treksoft.kvision.DomSpec
import kotlin.browser.document
@@ -57,51 +56,22 @@ class StateBindingSpec : DomSpec {
val store = createReduxStore(::stateReducer, State(10))
val container = SimplePanel()
- container.stateBinding(store) { state ->
+ container.bind(store) { state ->
div("${state.counter}")
}
root.add(container)
val element = document.getElementById("test")
assertEqualsHtml(
- "<div><div></div><div>10</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>",
+ "<div><div>11</div></div>",
element?.innerHTML,
"Should render changed state of the container"
)
}
}
-
- @Test
- fun stateUpdate() {
- run {
- val root = Root("test", fixed = 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"
- )
- }
- }
-
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index a1d37541..2d2fac29 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -33,6 +33,7 @@ import pl.treksoft.kvision.KVManager
import pl.treksoft.kvision.i18n.I18n
import pl.treksoft.kvision.i18n.I18n.trans
import pl.treksoft.kvision.panel.Root
+import pl.treksoft.kvision.state.ObservableState
import pl.treksoft.kvision.utils.SnOn
import pl.treksoft.kvision.utils.emptyOn
import pl.treksoft.kvision.utils.hooks
@@ -70,22 +71,27 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
field = value
if (oldField != field) refresh()
}
+
/**
* A title attribute of generated HTML element.
*/
var title: String? by refreshOnUpdate()
+
/**
* An ID attribute of generated HTML element.
*/
var id: String? by refreshOnUpdate()
+
/**
* A role attribute of generated HTML element.
*/
var role: String? by refreshOnUpdate()
+
/**
* A tabindex attribute of generated HTML element.
*/
var tabindex: Int? by refreshOnUpdate()
+
/**
* Determines if the current widget is draggable.
*/
@@ -111,8 +117,18 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
protected var lastLanguage: String? = null
+ /**
+ * A function called after the widget is inserted to the DOM.
+ */
var afterInsertHook: ((VNode) -> Unit)? = null
+ /**
+ * A function called after the widget is removed from the DOM.
+ */
var afterDestroyHook: (() -> Unit)? = null
+ /**
+ * A function called after the widget is disposed.
+ */
+ var afterDisposeHook: (() -> Unit)? = null
protected fun <T> singleRender(block: () -> T): T {
getRoot()?.renderDisabled = true
@@ -758,6 +774,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
}
override fun dispose() {
+ afterDisposeHook?.invoke()
}
protected fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }): RefreshDelegateProvider<T> =
@@ -797,6 +814,26 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component
companion object {
private var counter: Long = 0
+
+ /**
+ * 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(
+ observableState: ObservableState<S>,
+ factory: (W.(S) -> Unit)
+ ): W {
+ val unsubscribe = observableState.subscribe {
+ this.singleRender {
+ (this as? Container)?.removeAll()
+ this.factory(it)
+ }
+ }
+ this.afterDisposeHook = {
+ unsubscribe()
+ }
+ return this
+ }
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt b/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
index 20cb2d8e..d330f76d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/state/StateBinding.kt
@@ -5,6 +5,22 @@ package pl.treksoft.kvision.state
import pl.treksoft.kvision.core.Container
import pl.treksoft.kvision.core.Widget
+import pl.treksoft.kvision.core.Widget.Companion.bindState
+
+/**
+ * An extension function which binds the widget to the observable state.
+ *
+ * @param S the state type
+ * @param W the widget type
+ * @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(
+ observableState: ObservableState<S>,
+ factory: (W.(S) -> Unit)
+): W {
+ return this.bindState(observableState, factory)
+}
/**
* A class which binds the given container to the observable state.
@@ -16,6 +32,7 @@ import pl.treksoft.kvision.core.Widget
* @param container the container
* @param factory a function which re-creates the view based on the given state
*/
+@Deprecated("Use bind function instead.")
class StateBinding<S : Any, CONT : Container, CONTENT>(
observableState: ObservableState<S>,
private val container: CONT,
@@ -64,6 +81,11 @@ class StateBinding<S : Any, CONT : Container, CONTENT>(
*
* It takes the same parameters as the constructor of the built component.
*/
+@Suppress("DEPRECATION")
+@Deprecated(
+ "Use bind function instead.",
+ replaceWith = ReplaceWith("bind(observableState, factory)", "pl.treksoft.kvision.state.bind")
+)
fun <S : Any, CONT : Container> CONT.stateBinding(
observableState: ObservableState<S>,
factory: (CONT.(S) -> Unit)
@@ -76,6 +98,8 @@ fun <S : Any, CONT : Container> CONT.stateBinding(
*
* It takes the same parameters as the constructor of the built component.
*/
+@Suppress("DEPRECATION")
+@Deprecated("Use bind function instead.")
fun <S : Any, CONT : Container, CONTENT> CONT.stateUpdate(
observableState: ObservableState<S>,
factory: (CONT.(S) -> CONTENT)
@@ -86,6 +110,7 @@ fun <S : Any, CONT : Container, CONTENT> CONT.stateUpdate(
/**
* A helper class for updateable content.
*/
+@Deprecated("Use bind function instead.")
class Updateable<S : Any, CONTENT>(private val setUpdateState: ((S, CONTENT) -> Unit) -> Unit) {
infix fun updateWith(updateState: (S, CONTENT) -> Unit) {
setUpdateState(updateState)
diff --git a/src/test/kotlin/test/pl/treksoft/kvision/state/StateBindingSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/state/StateBindingSpec.kt
index c0de7c6f..940fd2cd 100644
--- a/src/test/kotlin/test/pl/treksoft/kvision/state/StateBindingSpec.kt
+++ b/src/test/kotlin/test/pl/treksoft/kvision/state/StateBindingSpec.kt
@@ -24,6 +24,7 @@ package test.pl.treksoft.kvision.state
import pl.treksoft.kvision.html.div
import pl.treksoft.kvision.panel.Root
import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.state.bind
import pl.treksoft.kvision.state.observableListOf
import pl.treksoft.kvision.state.stateBinding
import pl.treksoft.kvision.state.stateUpdate
@@ -34,6 +35,35 @@ import kotlin.test.Test
class StateBindingSpec : DomSpec {
@Test
+ fun bind() {
+ run {
+ val root = Root("test", fixed = true)
+ val container = SimplePanel()
+ val observableList = observableListOf(1, 2, 3)
+ container.bind(observableList) { state ->
+ setAttribute("data-count", "${state.size}")
+ state.forEach {
+ div("$it")
+ }
+ }
+ root.add(container)
+ val element = document.getElementById("test")
+ assertEqualsHtml(
+ "<div data-count=\"3\"><div>1</div><div>2</div><div>3</div></div>",
+ element?.innerHTML,
+ "Should render initial state of the widget"
+ )
+ observableList.add(4)
+ assertEqualsHtml(
+ "<div data-count=\"4\"><div>1</div><div>2</div><div>3</div><div>4</div></div>",
+ element?.innerHTML,
+ "Should render changed state of the widget"
+ )
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ @Test
fun stateBinding() {
run {
val root = Root("test", fixed = true)
@@ -60,6 +90,7 @@ class StateBindingSpec : DomSpec {
}
}
+ @Suppress("DEPRECATION")
@Test
fun stateUpdate() {
run {