elmish.tree.TreeView.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package elmish.tree
import data.mapAt
import elmish.View
import elmish.attributes
import elmish.div
import elmish.empty
import elmish.li
import elmish.ul
object TreeView {
data class Model(
val tree: Tree
)
sealed class Intent {
data class Toggle(val focus: Tree.Focus) : Intent()
}
fun view(model: Model): View> =
viewTree>(model.tree.focus()) { focus ->
div(
attributes {
onClick { Intent.Toggle(focus) }
},
focus.tree.label.toString()
)
}
fun step(intent: Intent, model: Model): Model = when (intent) {
is Intent.Toggle -> model.copy(
tree = intent.focus.update {
copy(state = state.toggle())
}
)
}
}
/**
* A persistent tree view model.
*/
data class Tree(
val label: T,
val children: List> = emptyList(),
val state: ViewState = ViewState.Collapsed
) {
enum class ViewState {
Collapsed,
Expanded;
fun toggle(): ViewState = when (this) {
Collapsed -> Expanded
Expanded -> Collapsed
}
}
/**
* Creates an updatable reference to this tree.
*/
fun focus(): Focus = Focus.Original(
this
)
fun isNotEmpty(): Boolean =
children.isNotEmpty()
/**
* Propagates changes to a particular node in the tree all the way
* up to the root (the [focused][focus] tree).
*/
sealed class Focus {
abstract val tree: Tree
abstract fun update(f: Tree.() -> Tree): Tree
val children
get() = tree.children.indices.asSequence().map(::child)
fun child(index: Int): Focus = Child(
this,
index,
tree.children[index]
)
data class Original(
override val tree: Tree
) : Focus() {
override fun update(f: Tree.() -> Tree): Tree = f(tree)
}
data class Child(
val parent: Focus,
val index: Int,
override val tree: Tree
) : Focus() {
override fun update(f: Tree.() -> Tree): Tree = parent.update {
copy(children = children.mapAt(index, f))
}
}
}
}
fun viewTree(
focus: Tree.Focus,
viewLabel: (Tree.Focus) -> View
): View = ul(
viewSubTree(focus, viewLabel)
)
fun viewSubTree(
focus: Tree.Focus,
viewLabel: (Tree.Focus) -> View
): View = focus.tree.run {
li(
viewLabel(focus),
children.takeIf { state == Tree.ViewState.Expanded && it.isNotEmpty() }?.run {
viewExpanded(focus, viewLabel)
} ?: empty
)
}
fun viewExpanded(focus: Tree.Focus, viewLabel: Tree.Focus.() -> View): View =
ul(viewChildrenOf(focus, viewLabel))
fun viewChildrenOf(
focus: Tree.Focus,
viewLabel: (Tree.Focus) -> View
): List> = viewSubTrees(focus.children, viewLabel)
fun viewSubTrees(
subTrees: Sequence>,
viewLabel: (Tree.Focus) -> View
): List> = subTrees
.map { subTree -> viewSubTree(subTree, viewLabel) }
.toList()
© 2015 - 2025 Weber Informatics LLC | Privacy Policy