diff options
author | Robert Jaros <rjaros@finn.pl> | 2019-03-14 15:22:23 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2019-03-14 15:22:23 +0100 |
commit | cb27ed25b9e5a448a3de95be41733e90d75d5933 (patch) | |
tree | 5b365218e83cb88be961ddf7af1a8d0b362aaa05 /src | |
parent | 56f45a8043c24491da9990e10d1cdc9cc22d4eca (diff) | |
download | kvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.tar.gz kvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.tar.bz2 kvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.zip |
Refactor DataContainer component to the separate module.
Diffstat (limited to 'src')
5 files changed, 1 insertions, 369 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index 82a3db48..9a1e556d 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -97,7 +97,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { protected var lastLanguage: String? = null - internal fun <T> singleRender(block: () -> T): T { + protected fun <T> singleRender(block: () -> T): T { getRoot()?.renderDisabled = true val t = block() getRoot()?.renderDisabled = false diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt deleted file mode 100644 index 4e5b92d5..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.data - -import kotlin.properties.ObservableProperty -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -/** - * Base interface for observable data model. - */ -interface DataComponent { - /** - * @suppress - * Internal property - */ - var container: DataUpdatable? - - /** - * @suppress - * Internal function for observable properties - */ - fun <T> obs(initialValue: T): ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) { - override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { - container?.update() - } - } -} - -/** - * Base abstract class for creating observable data model. - */ -abstract class BaseDataComponent : DataComponent { - override var container: DataUpdatable? = null -} diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt deleted file mode 100644 index 69851b29..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.data - -import com.github.snabbdom.VNode -import com.lightningkite.kotlin.observable.list.ObservableList -import pl.treksoft.kvision.core.Component -import pl.treksoft.kvision.core.Container -import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.panel.VPanel - -/** - * Sorter types. - */ -enum class SorterType { - ASC, - DESC -} - -/** - * A container class with support for observable data model. - * - * @constructor Creates DataContainer bound to given data model. - * @param M data model type - * @param C visual component type - * @param CONT container type - * @param model data model of type *ObservableList<M>* - * @param factory a function which creates component C from data model at given index - * @param container internal container - * @param containerAdd function to add component C to the internal container CONT - * @param filter a filtering function - * @param sorter a sorting function - * @param sorterType a sorting type selection function - * @param init an initializer extension function - */ -class DataContainer<M, C : Component, CONT : Container>( - private val model: ObservableList<M>, - private val factory: (M, Int, ObservableList<M>) -> C, - private val container: CONT, - private val containerAdd: (CONT.(C, M) -> Unit)? = null, - private val filter: ((M) -> Boolean)? = null, - private val sorter: ((M) -> Comparable<*>?)? = null, - private val sorterType: () -> SorterType = { SorterType.ASC }, - init: (DataContainer<M, C, CONT>.() -> Unit)? = null -) : - Widget(setOf()), Container, DataUpdatable { - - override var visible - get() = container.visible - set(value) { - container.visible = value - } - - internal var onUpdateHandler: (() -> Unit)? = null - - init { - container.parent = this - model.onUpdate += { - update() - } - update() - @Suppress("LeakingThis") - init?.invoke(this) - } - - override fun add(child: Component): Container { - this.container.add(child) - return this - } - - override fun addAll(children: List<Component>): Container { - this.container.addAll(children) - return this - } - - override fun remove(child: Component): Container { - this.container.remove(child) - return this - } - - override fun removeAll(): Container { - this.container.removeAll() - return this - } - - override fun getChildren(): List<Component> { - return this.container.getChildren() - } - - override fun renderVNode(): VNode { - return this.container.renderVNode() - } - - /** - * Updates view from the current data model state. - */ - @Suppress("ComplexMethod") - override fun update() { - model.forEach { - if (it is DataComponent) it.container = this - } - singleRender { - container.removeAll() - val indexed = model.mapIndexed { index, m -> m to index } - val sorted = if (sorter != null) { - when (sorterType()) { - SorterType.ASC -> - indexed.sortedBy { - @Suppress("UNCHECKED_CAST") - sorter.invoke(it.first) as Comparable<Any>? - } - SorterType.DESC -> - indexed.sortedByDescending { - @Suppress("UNCHECKED_CAST") - sorter.invoke(it.first) as Comparable<Any>? - } - } - } else { - indexed - } - val filtered = if (filter != null) { - sorted.filter { filter.invoke(it.first) } - } else { - sorted - } - val children = filtered.map { p -> p.first to factory(p.first, p.second, model) } - if (containerAdd != null) { - children.forEach { child -> - containerAdd.invoke(container, child.second, child.first) - } - } else { - container.addAll(children.map { it.second }) - } - } - onUpdateHandler?.invoke() - } - - /** - * Sets a notification handler called after every update. - * @param handler notification handler - * @return current container - */ - fun onUpdate(handler: () -> Unit): DataContainer<M, C, CONT> { - onUpdateHandler = handler - return this - } - - /** - * Clears notification handler. - * @return current container - */ - fun clearOnUpdate(): DataContainer<M, C, CONT> { - onUpdateHandler = null - return this - } - - companion object { - /** - * DSL builder extension function. - * - * It takes the same parameters as the constructor of the built component. - */ - fun <M, C : Component, CONT : Container> Container.dataContainer( - model: ObservableList<M>, - factory: (M, Int, ObservableList<M>) -> C, - container: CONT, - containerAdd: (CONT.(C, M) -> Unit)? = null, - filter: ((M) -> Boolean)? = null, - sorter: ((M) -> Comparable<*>?)? = null, - sorterType: () -> SorterType = { SorterType.ASC }, - init: (DataContainer<M, C, CONT>.() -> Unit)? = null - ): DataContainer<M, C, CONT> { - val dataContainer = DataContainer(model, factory, container, containerAdd, filter, sorter, sorterType, init) - this.add(dataContainer) - return dataContainer - } - - /** - * DSL builder extension function with VPanel default. - * - * It takes the same parameters as the constructor of the built component. - */ - fun <M, C : Component> Container.dataContainer( - model: ObservableList<M>, - factory: (M, Int, ObservableList<M>) -> C, - containerAdd: (VPanel.(C, M) -> Unit)? = null, - filter: ((M) -> Boolean)? = null, - sorter: ((M) -> Comparable<*>?)? = null, - sorterType: () -> SorterType = { SorterType.ASC }, - init: (DataContainer<M, C, VPanel>.() -> Unit)? = null - ): DataContainer<M, C, VPanel> { - val dataContainer = DataContainer(model, factory, VPanel(), containerAdd, filter, sorter, sorterType, init) - this.add(dataContainer) - return dataContainer - } - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt b/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt deleted file mode 100644 index 7c54e864..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package pl.treksoft.kvision.data - -/** - * Interface for updatable container. - */ -interface DataUpdatable { - fun update() -} diff --git a/src/test/kotlin/test/pl/treksoft/kvision/data/DataContainerSpec.kt b/src/test/kotlin/test/pl/treksoft/kvision/data/DataContainerSpec.kt deleted file mode 100644 index 931294d5..00000000 --- a/src/test/kotlin/test/pl/treksoft/kvision/data/DataContainerSpec.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017-present Robert Jaros - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package test.pl.treksoft.kvision.data - -import com.lightningkite.kotlin.observable.list.observableListOf -import pl.treksoft.kvision.data.BaseDataComponent -import pl.treksoft.kvision.data.DataContainer -import pl.treksoft.kvision.html.Label -import pl.treksoft.kvision.panel.Root -import pl.treksoft.kvision.panel.VPanel -import test.pl.treksoft.kvision.DomSpec -import kotlin.browser.document -import kotlin.test.Test - -class DataContainerSpec : DomSpec { - - @Test - fun render() { - run { - val root = Root("test", true) - - class Model(value: String) : BaseDataComponent() { - var value: String by obs(value) - } - - val model = observableListOf(Model("First"), Model("Second")) - val container = DataContainer(model, { m, _, _ -> Label(m.value) }, VPanel()) - root.add(container) - val element = document.getElementById("test") - assertEqualsHtml( - "<div style=\"display: flex; flex-direction: column;\"><div><span>First</span></div><div><span>Second</span></div></div>", - element?.innerHTML, - "Should render correct data container" - ) - model.add(Model("Third")) - assertEqualsHtml( - "<div style=\"display: flex; flex-direction: column;\"><div><span>First</span></div><div><span>Second</span></div><div><span>Third</span></div></div>", - element?.innerHTML, - "Should render correct data container after model change" - ) - model[1].value = "Changed" - assertEqualsHtml( - "<div style=\"display: flex; flex-direction: column;\"><div><span>First</span></div><div><span>Changed</span></div><div><span>Third</span></div></div>", - element?.innerHTML, - "Should render correct data container after model element change" - ) - } - } - -}
\ No newline at end of file |