Skip to content

🌳 A multiplatform tree view for Jetpack Compose

License

Notifications You must be signed in to change notification settings

adrielcafe/bonsai

Folders and files

NameName
Last commit message
Last commit date
Apr 13, 2022
Feb 7, 2024
Apr 20, 2022
Apr 20, 2022
Sep 29, 2023
Sep 29, 2023
Apr 21, 2022
Apr 2, 2022
Apr 2, 2022
Apr 2, 2022
Apr 21, 2022
Sep 29, 2023
Apr 7, 2022
Apr 5, 2022
Apr 21, 2022
Apr 2, 2022
Apr 2, 2022
Apr 15, 2022

Repository files navigation

Maven metadata URL Android API kotlin ktlint License MIT


Bonsai

A batteries-included Tree View for Jetpack Compose

Features

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:

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

Usage

Bonsai comes with a handy DSL for creating high-performance, customizable trees:

  1. Start by creating a new tree with Tree<T>{}
  2. Create nodes with Leaf<T>() and Branch<T>()
  3. Call Bonsai() to render the tree
@Composable
fun BonsaiExample() {
    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)
}

Output:

Take a look at the sample app for working examples.

File System integration

Import cafe.adriel.bonsai:bonsai-file-system module to use it.

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(
    tree = tree,
    // Custom style
    style = FileSystemBonsaiStyle()
)

Output:

JSON integration

Import cafe.adriel.bonsai:bonsai-json module to use it.

val tree = JsonTree(
    // Sample JSON from https://gateway.marvel.com/v1/public/characters
    json = responseJson
)

Bonsai(
    tree = tree,
    // Custom style
    style = JsonBonsaiStyle()
)

Output:

Expanding & Collapsing

Easily control the expanded/collapsed state of your Tree:

  • toggleExpansion(node)
  • collapseRoot() / expandRoot()
  • collapseAll() / expandAll()
  • collapseFrom(depth) / expandUntil(depth)
  • collapseNode(node) / expandNode(node)

Selecting

Selected/Unselected state is also pretty simple to control:

  • selectedNodes
  • toggleSelection(node)
  • selectNode(node) / unselectNode(node)
  • clearSelection()

Click handling

Its also possible to set custom click behaviors for your Tree. Control single, double and long clicks by using the expand and select APIs.

Bonsai(
    tree = tree,
    onClick = { node ->
        tree.clearSelection()
        tree.toggleExpansion(node)
    },
    onDoubleClick = { node -> /* ... */ },
    onLongClick = { node -> /* ... */ }
)

Styling

Change your Tree appearance as you wish. Take a look at BonsaiStyle class for all available customizations.

Bonsai(
    tree = tree,
    style = BonsaiStyle(
        toggleIconRotationDegrees = 0f,
        toggleIcon = { node ->
            rememberVectorPainter(
                if (node is BranchNode && node.isExpanded) Icons.Outlined.UnfoldLess
                else Icons.Outlined.UnfoldMore
            )
        },
        nodeIconSize = 18.dp,
        nodeShape = CutCornerShape(percent = 20),
        nodeCollapsedIcon = { rememberVectorPainter(Icons.Outlined.Circle) },
        nodeExpandedIcon = { rememberVectorPainter(Icons.Outlined.Adjust) },
        nodeNameTextStyle = MaterialTheme.typography.overline
    )
)

Output:

Custom nodes

Need a deeper customization? You can set customIcons and customNames for each Leaf<T>() and Branch<T>() nodes.

Leaf(
    content = "Wolf", 
    customIcon = { EmojiIcon("🐺") }
)

Output: