{"id":19373296,"url":"https://github.com/adrielcafe/bonsai","last_synced_at":"2025-04-05T03:10:22.911Z","repository":{"id":41868977,"uuid":"477116300","full_name":"adrielcafe/bonsai","owner":"adrielcafe","description":":deciduous_tree: A multiplatform tree view for Jetpack Compose","archived":false,"fork":false,"pushed_at":"2024-04-08T16:20:27.000Z","size":281,"stargazers_count":385,"open_issues_count":14,"forks_count":16,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-29T02:06:34.245Z","etag":null,"topics":["android","android-library","android-ui","compose","desktop","expandable","jetpack-compose","kotlin","kotlin-android","kotlin-dsl","kotlin-multiplatform","selectable","tree","tree-structure","treeview"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adrielcafe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"ko_fi":"adrielcafe"}},"created_at":"2022-04-02T16:54:02.000Z","updated_at":"2025-03-23T23:37:58.000Z","dependencies_parsed_at":"2022-08-01T01:48:50.057Z","dependency_job_id":"53bad808-d528-45bb-938e-ea4025ba9aae","html_url":"https://github.com/adrielcafe/bonsai","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbonsai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbonsai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbonsai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrielcafe%2Fbonsai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrielcafe","download_url":"https://codeload.github.com/adrielcafe/bonsai/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247280272,"owners_count":20912967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-library","android-ui","compose","desktop","expandable","jetpack-compose","kotlin","kotlin-android","kotlin-dsl","kotlin-multiplatform","selectable","tree","tree-structure","treeview"],"created_at":"2024-11-10T08:27:37.933Z","updated_at":"2025-04-05T03:10:22.895Z","avatar_url":"https://github.com/adrielcafe.png","language":"Kotlin","funding_links":["https://ko-fi.com/adrielcafe"],"categories":[],"sub_categories":[],"readme":"[![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=blue\u0026metadataUrl=https://s01.oss.sonatype.org/service/local/repo_groups/public/content/cafe/adriel/bonsai/bonsai-core/maven-metadata.xml\u0026style=for-the-badge)](https://repo.maven.apache.org/maven2/cafe/adriel/bonsai/)\n[![Android API](https://img.shields.io/badge/api-21%2B-brightgreen.svg?style=for-the-badge)](https://android-arsenal.com/api?level=21)\n[![kotlin](https://img.shields.io/github/languages/top/adrielcafe/bonsai.svg?style=for-the-badge\u0026color=blueviolet)](https://kotlinlang.org/)\n[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg?style=for-the-badge)](https://ktlint.github.io/)\n[![License MIT](https://img.shields.io/github/license/adrielcafe/voyager.svg?style=for-the-badge\u0026color=orange)](https://opensource.org/licenses/MIT)\n\n\u003ch1 align=\"center\"\u003e\n    \u003cimg height=\"80\" src=\"https://user-images.githubusercontent.com/2512298/163075335-494c2a4d-e446-4627-8865-183683a75834.png\"/\u003e\n    \u003cbr\u003e\n    Bonsai\n\u003c/h1\u003e\n\n\u003ch3  align=\"center\"\u003e\n    A batteries-included Tree View for Jetpack Compose\n    \u003cbr\u003e\u003cbr\u003e\n    \u003cimg width=400 src=\"https://user-images.githubusercontent.com/2512298/163289815-d584327c-ee5b-4f58-835c-121585b81f37.gif\"\u003e\n\u003c/h3\u003e\n\n#### Features\n- [x] Multiplatform: Android, Desktop\n- [x] State-aware: changes in the tree will trigger recomposition\n- [x] Unlimited depth\n- [x] Lazy loaded nodes\n- [x] Survives activity recreation\n- [x] [Built-in DSL](#usage)\n- [x] [File System integration](#file-system-integration)\n- [x] [JSON integration](#json-integration)\n- [x] [Expandable](#expanding--collapsing)\n- [x] [Selectable](#selecting)\n- [x] [Clickable](#click-handling)\n- [x] [Styleable](#styling)\n- [x] [Extendable](#custom-nodes)\n\n#### Roadmap\n- iOS support\n- Draggable nodes\n- FileObserver (Android) and/or WatchService (JVM) integration\n\n## Import to your project\nAdd the desired dependencies to your module's `build.gradle`:\n```gradle\nimplementation \"cafe.adriel.bonsai:bonsai-core:${latest-version}\"\nimplementation \"cafe.adriel.bonsai:bonsai-file-system:${latest-version}\"\nimplementation \"cafe.adriel.bonsai:bonsai-json:${latest-version}\"\n```\n\nCurrent version: ![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=blue\u0026metadataUrl=https://s01.oss.sonatype.org/service/local/repo_groups/public/content/cafe/adriel/bonsai/bonsai-core/maven-metadata.xml)\n\n## Usage\nBonsai comes with a handy DSL for creating high-performance, customizable trees:\n1. Start by creating a new tree with `Tree\u003cT\u003e{}`\n2. Create nodes with `Leaf\u003cT\u003e()` and `Branch\u003cT\u003e()`\n3. Call `Bonsai()` to render the tree\n```kotlin\n@Composable\nfun BonsaiExample() {\n    val tree = Tree {\n        Branch(\"Mammalia\") {\n            Branch(\"Carnivora\") {\n                Branch(\"Canidae\") {\n                    Branch(\"Canis\") {\n                        Leaf(\"Wolf\", customIcon = { EmojiIcon(\"🐺\") })\n                        Leaf(\"Dog\", customIcon = { EmojiIcon(\"🐶\") })\n                    }\n                }\n                Branch(\"Felidae\") {\n                    Branch(\"Felis\") {\n                        Leaf(\"Cat\", customIcon = { EmojiIcon(\"🐱\") })\n                    }\n                    Branch(\"Panthera\") {\n                        Leaf(\"Lion\", customIcon = { EmojiIcon(\"🦁\") })\n                    }\n                }\n            }\n        }\n    }\n\n    Bonsai(tree)\n}\n```\n\nOutput:\n\n\u003cimg width=200 src=\"https://user-images.githubusercontent.com/2512298/164339464-2fc48395-8b52-414d-93d0-b4829dcc5c76.png\"\u003e\n\n**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.**\n\n### File System integration\nImport `cafe.adriel.bonsai:bonsai-file-system` module to use it.\n\n```kotlin\nval tree = FileSystemTree(\n    // Also works with java.nio.file.Path and okio.Path\n    rootPath = File(path),\n    // To show or not the root directory in the tree\n    selfInclude = true\n)\n\nBonsai(\n    tree = tree,\n    // Custom style\n    style = FileSystemBonsaiStyle()\n)\n```\n\nOutput:\n\n\u003cimg width=350 src=\"https://user-images.githubusercontent.com/2512298/164344584-d17d3131-6515-4d76-b7c1-017e33731c6f.png\"\u003e\n\n### JSON integration\nImport `cafe.adriel.bonsai:bonsai-json` module to use it.\n\n```kotlin\nval tree = JsonTree(\n    // Sample JSON from https://gateway.marvel.com/v1/public/characters\n    json = responseJson\n)\n\nBonsai(\n    tree = tree,\n    // Custom style\n    style = JsonBonsaiStyle()\n)\n```\n\nOutput:\n\n\u003cimg width=400 src=\"https://user-images.githubusercontent.com/2512298/164342551-b5f869eb-a2fd-414c-b856-916601e2785e.png\"\u003e \u003cimg width=400 src=\"https://user-images.githubusercontent.com/2512298/164344465-6a0fb267-cd26-458c-a67b-db18d6a45d03.png\"\u003e\n\n### Expanding \u0026 Collapsing\nEasily control the expanded/collapsed state of your `Tree`: \n* `toggleExpansion(node)`\n* `collapseRoot()` / `expandRoot()`\n* `collapseAll()` / `expandAll()`\n* `collapseFrom(depth)` / `expandUntil(depth)`\n* `collapseNode(node)` / `expandNode(node)`\n\n### Selecting\nSelected/Unselected state is also pretty simple to control: \n* `selectedNodes`\n* `toggleSelection(node)`\n* `selectNode(node)` / `unselectNode(node)`\n* `clearSelection()`\n\n### Click handling\nIts also possible to set custom click behaviors for your `Tree`. Control single, double and long clicks by using the [expand](#expanding--collapsing) and [select](#selecting) APIs.\n```kotlin\nBonsai(\n    tree = tree,\n    onClick = { node -\u003e\n        tree.clearSelection()\n        tree.toggleExpansion(node)\n    },\n    onDoubleClick = { node -\u003e /* ... */ },\n    onLongClick = { node -\u003e /* ... */ }\n)\n```\n\n### Styling\nChange your `Tree` appearance as you wish. Take a look at `BonsaiStyle` class for all available customizations.\n```kotlin\nBonsai(\n    tree = tree,\n    style = BonsaiStyle(\n        toggleIconRotationDegrees = 0f,\n        toggleIcon = { node -\u003e\n            rememberVectorPainter(\n                if (node is BranchNode \u0026\u0026 node.isExpanded) Icons.Outlined.UnfoldLess\n                else Icons.Outlined.UnfoldMore\n            )\n        },\n        nodeIconSize = 18.dp,\n        nodeShape = CutCornerShape(percent = 20),\n        nodeCollapsedIcon = { rememberVectorPainter(Icons.Outlined.Circle) },\n        nodeExpandedIcon = { rememberVectorPainter(Icons.Outlined.Adjust) },\n        nodeNameTextStyle = MaterialTheme.typography.overline\n    )\n)\n```\n\nOutput:\n\n\u003cimg width=250 src=\"https://user-images.githubusercontent.com/2512298/164343259-0df574f6-e602-4bb1-93e2-c263fb1ee1ed.png\"\u003e\n\n### Custom nodes\nNeed a deeper customization? You can set `customIcon`s and `customName`s for each `Leaf\u003cT\u003e()` and `Branch\u003cT\u003e()` nodes.\n```kotlin\nLeaf(\n    content = \"Wolf\", \n    customIcon = { EmojiIcon(\"🐺\") }\n)\n```\n\nOutput:\n\n\u003cimg width=200 src=\"https://user-images.githubusercontent.com/2512298/164343479-fd6d2ce0-4335-48ac-90a0-0a93c2c57649.png\"\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrielcafe%2Fbonsai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrielcafe%2Fbonsai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrielcafe%2Fbonsai/lists"}