aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2019-03-14 15:22:23 +0100
committerRobert Jaros <rjaros@finn.pl>2019-03-14 15:22:23 +0100
commitcb27ed25b9e5a448a3de95be41733e90d75d5933 (patch)
tree5b365218e83cb88be961ddf7af1a8d0b362aaa05 /src
parent56f45a8043c24491da9990e10d1cdc9cc22d4eca (diff)
downloadkvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.tar.gz
kvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.tar.bz2
kvision-cb27ed25b9e5a448a3de95be41733e90d75d5933.zip
Refactor DataContainer component to the separate module.
Diffstat (limited to 'src')
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/core/Widget.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/data/DataComponent.kt54
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/data/DataContainer.kt216
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/data/DataUpdatable.kt29
-rw-r--r--src/test/kotlin/test/pl/treksoft/kvision/data/DataContainerSpec.kt69
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