diff --git a/README.md b/README.md index 4509813..16e6286 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,12 @@ #### Features - [x] Multiplatform: Android, Desktop - [x] State-aware: changes in the tree will trigger recomposition -- [x] Unlimited levels +- [x] Unlimited depth +- [x] Lazy loaded nodes +- [x] Survives activity recreation +- [x] [Built-in DSL](#usage) - [x] [File System integration](#file-system-integration) - [x] [JSON integration](#json-integration) -- [x] [Built-in DSL](#dsl) - [x] [Expandable](#expanding--collapsing) - [x] [Selectable](#selecting) - [x] [Clickable](#click-handling) @@ -31,30 +33,47 @@ #### Roadmap - iOS support +- Draggable nodes +- FileObserver (Android) and/or WatchService (JVM) integration + +## Import to your project +Add the desired dependencies to your module's `build.gradle`: +```gradle +implementation "cafe.adriel.bonsai:bonsai-core:${latest-version}" +implementation "cafe.adriel.bonsai:bonsai-file-system:${latest-version}" +implementation "cafe.adriel.bonsai:bonsai-json:${latest-version}" +``` + +Current version: ![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=blue&metadataUrl=https://s01.oss.sonatype.org/service/local/repo_groups/public/content/cafe/adriel/bonsai/bonsai-core/maven-metadata.xml) ## Usage -Start by creating a new tree with `rememberTree()`. Use `SimpleLeafNode` and `SimpleBranchNode` to create leaf and branch nodes, respectively. Call the `Bonsai()` composable to render the tree. +Bonsai comes with a handy DSL for creating high-performance, customizable trees: +1. Start by creating a new tree with `Tree{}` +2. Create nodes with `Leaf()` and `Branch()` +3. Call `Bonsai()` to render the tree ```kotlin @Composable fun BonsaiExample() { - val tree = rememberTree( - nodes = listOf( - SimpleLeafNode( - content = "A leaf node" - ), - SimpleBranchNode( - content = "A branch node", - children = { node -> - listOf( - SimpleLeafNode( - content = "A child node", - parent = node - ), - ) + val tree = Tree { + Branch("Mammalia") { + Branch("Carnivora") { + Branch("Canidae") { + Branch("Canis") { + Leaf("Wolf", customIcon = { EmojiIcon("🐺") }) + Leaf("Dog", customIcon = { EmojiIcon("🐶") }) + } } - ) - ) - ) + Branch("Felidae") { + Branch("Felis") { + Leaf("Cat", customIcon = { EmojiIcon("🐱") }) + } + Branch("Panthera") { + Leaf("Lion", customIcon = { EmojiIcon("🦁") }) + } + } + } + } + } Bonsai(tree) } @@ -62,21 +81,19 @@ fun BonsaiExample() { Output: - + -**Take a look at the [sample app](https://github.com/adrielcafe/bonsai/blob/main/sample/src/main/java/cafe/adriel/bonsai/sample/SampleActivity.kt) for a working example.** +**Take a look at the [sample app](https://github.com/adrielcafe/bonsai/blob/main/sample/src/main/java/cafe/adriel/bonsai/sample/) for working examples.** ### File System integration Import `cafe.adriel.bonsai:bonsai-file-system` module to use it. ```kotlin -val tree = rememberTree( - nodes = fileSystemNodes( - // Also works with java.nio.file.Path and okio.Path - rootPath = File(path), - // To show or not the root directory in the tree - selfInclude = true - ) +val tree = FileSystemTree( + // Also works with java.nio.file.Path and okio.Path + rootPath = File(path), + // To show or not the root directory in the tree + selfInclude = true ) Bonsai( @@ -88,15 +105,15 @@ Bonsai( Output: - + ### JSON integration Import `cafe.adriel.bonsai:bonsai-json` module to use it. ```kotlin -val tree = rememberTree( - // Sample JSON from https://rickandmortyapi.com/api/character - nodes = jsonNodes(json) +val tree = JsonTree( + // Sample JSON from https://gateway.marvel.com/v1/public/characters + json = responseJson ) Bonsai( @@ -108,47 +125,19 @@ Bonsai( Output: - - -### DSL -Looking for a simpler and less verbose way to create a tree? Here's a handy DSL for you. -```kotlin -val tree = Tree { - Branch("bonsai") { - Branch("bonsai-core") { - Branch("build") - Branch("src") { - Branch("androidMain") { - Leaf("AndroidManifest.xml") - } - Branch("commonMain") - } - Leaf("build.gradle") - } - Branch("bonsai-file-system") - Branch("sample") - Leaf(".gitignore") - Leaf("build.gradle") - Leaf("README.md") - } -} -``` - -Output: - - + ### Expanding & Collapsing -Easy control the expanded/collapsed state of your `Tree`: +Easily control the expanded/collapsed state of your `Tree`: * `toggleExpansion(node)` -* `collapseAll()` / `expandAll()` * `collapseRoot()` / `expandRoot()` -* `collapseFrom(minLevel)` / `expandUntil(maxLevel)` +* `collapseAll()` / `expandAll()` +* `collapseFrom(depth)` / `expandUntil(depth)` * `collapseNode(node)` / `expandNode(node)` ### Selecting Selected/Unselected state is also pretty simple to control: -* `selectedNodes` (observable state backed by `SnapshotStateList`) +* `selectedNodes` * `toggleSelection(node)` * `selectNode(node)` / `unselectNode(node)` * `clearSelection()` @@ -173,8 +162,6 @@ Change your `Tree` appearance as you wish. Take a look at `BonsaiStyle` class fo Bonsai( tree = tree, style = BonsaiStyle( - expandTransition = slideIn(), - collapseTransition = slideOut(), toggleIconRotationDegrees = 0f, toggleIcon = { node -> rememberVectorPainter( @@ -193,44 +180,17 @@ Bonsai( Output: - + ### Custom nodes -Need a deeper customization? Just extend the `SimpleLeafNode` and `SimpleBranchNode` classes, or even the `Node` interface, to fit your needs. +Need a deeper customization? You can set `customIcon`s and `customName`s for each `Leaf()` and `Branch()` nodes. ```kotlin -class CustomLeafNode : SimpleLeafNode(/* setup */) { - - @Composable - override fun BonsaiScope.NodeIcon() { - // Custom leaf node icon - } - - @Composable - override fun BonsaiScope.NodeName() { - // Custom leaf node name - } -} - -class CustomBranchNode : SimpleBranchNode(/* setup */) { - - @Composable - override fun BonsaiScope.NodeIcon() { - // Custom branch node icon - } - - @Composable - override fun BonsaiScope.NodeName() { - // Custom branch node name - } -} +Leaf( + content = "Wolf", + customIcon = { EmojiIcon("🐺") } +) ``` -## Import to your project -Add the desired dependencies to your module's `build.gradle`: -```gradle -implementation "cafe.adriel.bonsai:bonsai-core:${latest-version}" -implementation "cafe.adriel.bonsai:bonsai-file-system:${latest-version}" -implementation "cafe.adriel.bonsai:bonsai-json:${latest-version}" -``` +Output: -Current version: ![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=blue&metadataUrl=https://s01.oss.sonatype.org/service/local/repo_groups/public/content/cafe/adriel/bonsai/bonsai-core/maven-metadata.xml) + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index f17c9fb..e9652d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true # Maven GROUP=cafe.adriel.bonsai -VERSION_NAME=1.1.0 +VERSION_NAME=1.2.0 POM_DESCRIPTION=A multiplatform tree view for Jetpack Compose POM_INCEPTION_YEAR=2022 diff --git a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/DslTreeScreen.kt b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/DslTreeScreen.kt index 4bc76b1..ba8b909 100644 --- a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/DslTreeScreen.kt +++ b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/DslTreeScreen.kt @@ -15,7 +15,7 @@ object DslTreeScreen : TreeScreen { override val title = "DSL Tree" @Composable - override fun buildTree(): Tree = + override fun composeTree(): Tree = Tree { Branch("Animalia") { Branch("Chordata") { @@ -23,16 +23,16 @@ object DslTreeScreen : TreeScreen { Branch("Carnivora") { Branch("Canidae") { Branch("Canis") { - Leaf("Wolf", customIcon = { LeafIcon("\uD83D\uDC3A") }) - Leaf("Dog", customIcon = { LeafIcon("\uD83D\uDC36") }) + Leaf("Wolf", customIcon = { EmojiIcon("\uD83D\uDC3A") }) + Leaf("Dog", customIcon = { EmojiIcon("\uD83D\uDC36") }) } } Branch("Felidae") { Branch("Felis") { - Leaf("Cat", customIcon = { LeafIcon("\uD83D\uDC31") }) + Leaf("Cat", customIcon = { EmojiIcon("\uD83D\uDC31") }) } Branch("Panthera") { - Leaf("Lion", customIcon = { LeafIcon("\uD83E\uDD81") }) + Leaf("Lion", customIcon = { EmojiIcon("\uD83E\uDD81") }) } } } @@ -43,12 +43,12 @@ object DslTreeScreen : TreeScreen { Branch("Solanales") { Branch("Convolvulaceae") { Branch("Ipomoea") { - Leaf("Sweet Potato", customIcon = { LeafIcon("\uD83C\uDF60") }) + Leaf("Sweet Potato", customIcon = { EmojiIcon("\uD83C\uDF60") }) } } Branch("Solanaceae") { - Leaf("Potato", customIcon = { LeafIcon("\uD83E\uDD54") }) - Leaf("Tomato", customIcon = { LeafIcon("\uD83C\uDF45") }) + Leaf("Potato", customIcon = { EmojiIcon("\uD83E\uDD54") }) + Leaf("Tomato", customIcon = { EmojiIcon("\uD83C\uDF45") }) } } } @@ -69,7 +69,7 @@ object DslTreeScreen : TreeScreen { } @Composable - private fun LeafIcon(emoji: String) { + private fun EmojiIcon(emoji: String) { Text(emoji) } } diff --git a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/FileSystemTreeScreen.kt b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/FileSystemTreeScreen.kt index 3d97d1f..aa3ae69 100644 --- a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/FileSystemTreeScreen.kt +++ b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/FileSystemTreeScreen.kt @@ -31,7 +31,7 @@ object FileSystemTreeScreen : TreeScreen { override val title = "File System Tree" @Composable - override fun buildTree(): Tree { + override fun composeTree(): Tree { val context = LocalContext.current val rootDirectory = remember { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.dataDir diff --git a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/JsonTreeScreen.kt b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/JsonTreeScreen.kt index 7ef9de6..ce9e439 100644 --- a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/JsonTreeScreen.kt +++ b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/JsonTreeScreen.kt @@ -17,7 +17,7 @@ object JsonTreeScreen : TreeScreen { override val title = "JSON Tree" @Composable - override fun buildTree(): Tree { + override fun composeTree(): Tree { val context = LocalContext.current val json = context .resources diff --git a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/TreeScreen.kt b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/TreeScreen.kt index 89fc791..8d36043 100644 --- a/sample/src/main/java/cafe/adriel/bonsai/sample/tree/TreeScreen.kt +++ b/sample/src/main/java/cafe/adriel/bonsai/sample/tree/TreeScreen.kt @@ -34,7 +34,7 @@ interface TreeScreen : Screen { @Composable override fun Content() { - val tree = buildTree() + val tree = composeTree() Column( verticalArrangement = Arrangement.Bottom, @@ -52,7 +52,7 @@ interface TreeScreen : Screen { } @Composable - fun buildTree(): Tree + fun composeTree(): Tree @Composable fun BonsaiContent(