mirror of
https://github.com/AdrianKuta/Tree-Data-Structure.git
synced 2026-06-19 19:00:14 +02:00
Compare commits
3 Commits
1fce412815
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fe6535258 | ||
|
|
28c1690f96 | ||
|
|
dc59476b10 |
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -21,9 +21,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: JVM / JS / Wasm / Native + API check
|
||||
- name: JVM / JS / Wasm / Native / Android + API check
|
||||
os: ubuntu-latest
|
||||
tasks: jvmTest jsNodeTest wasmJsNodeTest nativeTest apiCheck :samples:test :samples:run
|
||||
tasks: jvmTest jsNodeTest wasmJsNodeTest nativeTest :assembleRelease :tree-structure-compose:assembleRelease :samples-android:assembleDebug :samples:test :samples:run apiCheck
|
||||
- name: iOS
|
||||
os: macos-latest
|
||||
tasks: iosSimulatorArm64Test
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -4,7 +4,20 @@ All notable changes to this project are documented here. The format is based on
|
||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [4.2.0] - 2026-06-08
|
||||
|
||||
### Added
|
||||
- **Android target.** The core `tree-structure` module and `tree-structure-compose` now publish an
|
||||
`-android` variant (`minSdk` 21), so both can be consumed directly in Android projects.
|
||||
- `tree-structure-compose`: a foundation-only `TreeNodeRow` composable — a sensible default node row
|
||||
(clickable, indented, with a `▾`/`▸` marker and no Material dependency) — plus a no-content
|
||||
`LazyTree(root, label = …)` overload that renders each node with it. The existing lambda overload
|
||||
is unchanged.
|
||||
- A runnable Android sample app in the new `samples` module, with `@Preview` composables.
|
||||
|
||||
### Changed
|
||||
- Upgraded the Gradle wrapper to 8.10.2 and introduced Android Gradle Plugin 8.7.2 (`compileSdk` 35).
|
||||
No source or behavior changes to existing targets.
|
||||
|
||||
## [4.1.1] - 2026-06-08
|
||||
|
||||
@@ -99,7 +112,7 @@ A breaking release that cleans up the core API and enforces an explicit public s
|
||||
## [3.1.3]
|
||||
- iOS targets and Maven Central (Sonatype Central Portal) publishing.
|
||||
|
||||
[Unreleased]: https://github.com/AdrianKuta/Tree-Data-Structure/compare/v4.1.1...HEAD
|
||||
[4.2.0]: https://github.com/AdrianKuta/Tree-Data-Structure/compare/v4.1.1...v4.2.0
|
||||
[4.1.1]: https://github.com/AdrianKuta/Tree-Data-Structure/compare/v4.1.0...v4.1.1
|
||||
[4.1.0]: https://github.com/AdrianKuta/Tree-Data-Structure/compare/v4.0.0...v4.1.0
|
||||
[4.0.0]: https://github.com/AdrianKuta/Tree-Data-Structure/compare/v3.4.0...v4.0.0
|
||||
|
||||
33
README.md
33
README.md
@@ -16,7 +16,7 @@ usually a better fit.
|
||||
|
||||
## Features
|
||||
|
||||
- Kotlin Multiplatform: JVM, JS, Wasm, iOS, and a native host target.
|
||||
- Kotlin Multiplatform: JVM, Android, JS, Wasm, iOS, and a native host target.
|
||||
- Build trees with a `tree { child(...) }` DSL or node by node with `addChild`.
|
||||
- Pre-order, post-order, and level-order traversal, as iterators or lazy `Sequence`s.
|
||||
- Navigation: `root()`, `ancestors()`, `siblings()`, `leaves()`, `descendants()`, `isLeaf`, `degree`.
|
||||
@@ -30,14 +30,14 @@ Gradle (Kotlin DSL):
|
||||
```kotlin
|
||||
// commonMain for KMP projects, or any sourceSet/module where you need it
|
||||
dependencies {
|
||||
implementation("com.github.adriankuta:tree-structure:4.1.1") // latest version is on the badge above
|
||||
implementation("com.github.adriankuta:tree-structure:4.2.0") // latest version is on the badge above
|
||||
}
|
||||
```
|
||||
|
||||
Gradle (Groovy):
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation "com.github.adriankuta:tree-structure:4.1.1"
|
||||
implementation "com.github.adriankuta:tree-structure:4.2.0"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -46,7 +46,7 @@ Maven:
|
||||
<dependency>
|
||||
<groupId>com.github.adriankuta</groupId>
|
||||
<artifactId>tree-structure</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<version>4.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -160,7 +160,7 @@ that depends on the core.
|
||||
`@Serializable` directly. Convert to and from the acyclic `TreeNodeDto` instead.
|
||||
|
||||
```kotlin
|
||||
implementation("com.github.adriankuta:tree-structure-serialization:4.1.1")
|
||||
implementation("com.github.adriankuta:tree-structure-serialization:4.2.0")
|
||||
```
|
||||
```kotlin
|
||||
val json = Json.encodeToString(root.toDto())
|
||||
@@ -172,7 +172,7 @@ val restored = Json.decodeFromString<TreeNodeDto<String>>(json).toTreeNode()
|
||||
Traverse a tree as a cold `Flow`, which is handy inside coroutine and `ViewModel` pipelines.
|
||||
|
||||
```kotlin
|
||||
implementation("com.github.adriankuta:tree-structure-coroutines:4.1.1")
|
||||
implementation("com.github.adriankuta:tree-structure-coroutines:4.2.0")
|
||||
```
|
||||
```kotlin
|
||||
root.preOrderFlow().collect { println(it.value) }
|
||||
@@ -181,12 +181,23 @@ root.asFlow(TreeNodeIterators.LevelOrder).map { it.value }
|
||||
|
||||
### Compose UI (`tree-structure-compose`)
|
||||
|
||||
A `LazyTree` composable for Compose Multiplatform (JVM/desktop, iOS, Wasm). Only the visible nodes
|
||||
are composed, and you decide how each node looks:
|
||||
A `LazyTree` composable for Compose Multiplatform (JVM/desktop, Android, iOS, Wasm). Only the visible
|
||||
nodes are composed.
|
||||
|
||||
```kotlin
|
||||
implementation("com.github.adriankuta:tree-structure-compose:4.1.1")
|
||||
implementation("com.github.adriankuta:tree-structure-compose:4.2.0")
|
||||
```
|
||||
|
||||
For the common case, the no-content overload renders each node with the built-in `TreeNodeRow`
|
||||
(a clickable, indented row with a `▾`/`▸` marker — foundation-only, no Material dependency):
|
||||
|
||||
```kotlin
|
||||
LazyTree(root) // sensible default
|
||||
LazyTree(root, label = { it.name }) // map a node's value to its text
|
||||
```
|
||||
|
||||
Or supply your own row for full control:
|
||||
|
||||
```kotlin
|
||||
LazyTree(root) { node, depth, expanded, toggle ->
|
||||
Row(Modifier.padding(start = (depth * 16).dp).clickable(onClick = toggle)) {
|
||||
@@ -196,6 +207,8 @@ LazyTree(root) { node, depth, expanded, toggle ->
|
||||
}
|
||||
```
|
||||
|
||||
A runnable Android demo lives in the [`samples-android`](samples-android) module.
|
||||
|
||||
### Immutable (`tree-structure-immutable`)
|
||||
|
||||
A persistent `ImmutableTreeNode` with structural sharing. Every operation (`addChild`,
|
||||
@@ -204,7 +217,7 @@ subtrees are reused, so updates are cheap and old roots stay valid. Backed by
|
||||
`kotlinx.collections.immutable`.
|
||||
|
||||
```kotlin
|
||||
implementation("com.github.adriankuta:tree-structure-immutable:4.1.1")
|
||||
implementation("com.github.adriankuta:tree-structure-immutable:4.2.0")
|
||||
```
|
||||
```kotlin
|
||||
val root = ImmutableTreeNode("World").addChild(ImmutableTreeNode("Europe"))
|
||||
|
||||
142
api/jvm/tree-structure.api
Normal file
142
api/jvm/tree-structure.api
Normal file
@@ -0,0 +1,142 @@
|
||||
public abstract interface class com/github/adriankuta/datastructure/tree/ChildDeclarationInterface {
|
||||
public abstract synthetic fun child (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/ChildDeclarationInterface$DefaultImpls {
|
||||
public static synthetic fun child$default (Lcom/github/adriankuta/datastructure/tree/ChildDeclarationInterface;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeConnectors {
|
||||
public static final field Companion Lcom/github/adriankuta/datastructure/tree/TreeConnectors$Companion;
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
public final fun component1 ()Ljava/lang/String;
|
||||
public final fun component2 ()Ljava/lang/String;
|
||||
public final fun component3 ()Ljava/lang/String;
|
||||
public final fun component4 ()Ljava/lang/String;
|
||||
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/github/adriankuta/datastructure/tree/TreeConnectors;
|
||||
public static synthetic fun copy$default (Lcom/github/adriankuta/datastructure/tree/TreeConnectors;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/github/adriankuta/datastructure/tree/TreeConnectors;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getBranch ()Ljava/lang/String;
|
||||
public final fun getEmpty ()Ljava/lang/String;
|
||||
public final fun getLastBranch ()Ljava/lang/String;
|
||||
public final fun getVertical ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeConnectors$Companion {
|
||||
public final fun getAscii ()Lcom/github/adriankuta/datastructure/tree/TreeConnectors;
|
||||
public final fun getDefault ()Lcom/github/adriankuta/datastructure/tree/TreeConnectors;
|
||||
}
|
||||
|
||||
public class com/github/adriankuta/datastructure/tree/TreeNode : com/github/adriankuta/datastructure/tree/ChildDeclarationInterface, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public fun <init> (Ljava/lang/Object;Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;)V
|
||||
public synthetic fun <init> (Ljava/lang/Object;Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun addChild (Lcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public final fun addChildren ([Lcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public synthetic fun child (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public final fun clear ()V
|
||||
public final fun depth ()I
|
||||
public final fun detach ()Z
|
||||
public final fun getChildren ()Ljava/util/List;
|
||||
public final fun getParent ()Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public final fun getTreeIterator ()Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
public final fun getValue ()Ljava/lang/Object;
|
||||
public final fun height ()I
|
||||
public final fun insertChild (ILcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public final fun isRoot ()Z
|
||||
public fun iterator ()Ljava/util/Iterator;
|
||||
public final fun iterator (Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;)Ljava/util/Iterator;
|
||||
public final fun moveChild (Lcom/github/adriankuta/datastructure/tree/TreeNode;I)Z
|
||||
public final fun nodeCount ()I
|
||||
public final fun path (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
public final fun prettyString ()Ljava/lang/String;
|
||||
public final fun removeChild (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Z
|
||||
public final fun removeChildAt (I)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public final fun replaceChild (ILcom/github/adriankuta/datastructure/tree/TreeNode;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public final fun sortChildren (Ljava/util/Comparator;)V
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeNodeFunctionalExtKt {
|
||||
public static final fun allNodes (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)Z
|
||||
public static final fun anyNode (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)Z
|
||||
public static final fun countNodes (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)I
|
||||
public static final fun deepCopy (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public static final fun filterNodes (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||
public static final fun findNode (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public static final fun foldNodes (Lcom/github/adriankuta/datastructure/tree/TreeNode;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
|
||||
public static final fun mapValues (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lkotlin/jvm/functions/Function1;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public static final fun structurallyEquals (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeNode;)Z
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeNodeNavigationExtKt {
|
||||
public static final fun ancestors (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
public static final fun descendants (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
public static final fun getDegree (Lcom/github/adriankuta/datastructure/tree/TreeNode;)I
|
||||
public static final fun isLeaf (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Z
|
||||
public static final fun leaves (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
public static final fun root (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public static final fun siblings (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeNodePrettyPrintExtKt {
|
||||
public static final fun prettyString (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeConnectors;Lkotlin/jvm/functions/Function3;)Ljava/lang/String;
|
||||
public static synthetic fun prettyString$default (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeConnectors;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeNodeQueryExtKt {
|
||||
public static final fun contains (Lcom/github/adriankuta/datastructure/tree/TreeNode;Ljava/lang/Object;)Z
|
||||
public static final fun distance (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/lang/Integer;
|
||||
public static final fun lowestCommonAncestor (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public static final fun pathBetween (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/TreeNode;)Ljava/util/List;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/TreeNodeSequenceExtKt {
|
||||
public static final fun asSequence (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;)Lkotlin/sequences/Sequence;
|
||||
public static synthetic fun asSequence$default (Lcom/github/adriankuta/datastructure/tree/TreeNode;Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
|
||||
public static final fun levelOrderSequence (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lkotlin/sequences/Sequence;
|
||||
public static final fun postOrderSequence (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lkotlin/sequences/Sequence;
|
||||
public static final fun preOrderSequence (Lcom/github/adriankuta/datastructure/tree/TreeNode;)Lkotlin/sequences/Sequence;
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/exceptions/TreeNodeException : java/lang/RuntimeException {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/iterators/LevelOrderTreeIterator : java/util/Iterator, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public fun <init> (Lcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public fun hasNext ()Z
|
||||
public fun next ()Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public synthetic fun next ()Ljava/lang/Object;
|
||||
public fun remove ()V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/iterators/PostOrderTreeIterator : java/util/Iterator, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public fun <init> (Lcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public fun hasNext ()Z
|
||||
public fun next ()Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public synthetic fun next ()Ljava/lang/Object;
|
||||
public fun remove ()V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/iterators/PreOrderTreeIterator : java/util/Iterator, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public fun <init> (Lcom/github/adriankuta/datastructure/tree/TreeNode;)V
|
||||
public fun hasNext ()Z
|
||||
public fun next ()Lcom/github/adriankuta/datastructure/tree/TreeNode;
|
||||
public synthetic fun next ()Ljava/lang/Object;
|
||||
public fun remove ()V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators : java/lang/Enum {
|
||||
public static final field LevelOrder Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
public static final field PostOrder Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
public static final field PreOrder Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public static fun valueOf (Ljava/lang/String;)Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
public static fun values ()[Lcom/github/adriankuta/datastructure/tree/iterators/TreeNodeIterators;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ plugins {
|
||||
alias(libs.plugins.binaryCompatibilityValidator)
|
||||
alias(libs.plugins.kover)
|
||||
signing
|
||||
alias(libs.plugins.androidLibrary)
|
||||
// Loaded once here (with a known version) so the Android application/library variants can be
|
||||
// applied across subprojects without the "already on the classpath with an unknown version" clash.
|
||||
alias(libs.plugins.androidApplication) apply false
|
||||
alias(libs.plugins.kotlinAndroid) apply false
|
||||
}
|
||||
|
||||
val PUBLISH_GROUP_ID = "com.github.adriankuta"
|
||||
val PUBLISH_ARTIFACT_ID = "tree-structure" // base artifact; KMP will add -jvm, -ios*, etc.
|
||||
val PUBLISH_VERSION = "4.1.1"
|
||||
val PUBLISH_VERSION = "4.2.0"
|
||||
|
||||
val snapshot: String? by project
|
||||
|
||||
@@ -57,11 +62,13 @@ mavenPublishing {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
apiValidation {
|
||||
// :samples is a dev-facing examples module, not a published artifact, so it has no .api dump.
|
||||
// Neither sample module is a published artifact, so neither has a .api dump.
|
||||
ignoredProjects.add("samples")
|
||||
ignoredProjects.add("samples-android")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -94,6 +101,15 @@ kotlin {
|
||||
|
||||
jvm()
|
||||
|
||||
androidTarget {
|
||||
publishLibraryVariants("release")
|
||||
// Build the Android variant at JVM 17 so Android consumers (JVM 11/17) can inline the
|
||||
// library's inline DSL (`tree { }`) — they cannot inline the default JVM-21 bytecode.
|
||||
compilerOptions {
|
||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
|
||||
js(IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
@@ -126,3 +142,15 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.github.adriankuta.datastructure.tree"
|
||||
compileSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.androidMinSdk.get().toInt()
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,9 @@ kotlin.code.style=official
|
||||
# Dokka Gradle Plugin v2 (https://kotl.in/dokka-gradle-migration)
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
|
||||
# Android: Compose pulls AndroidX artifacts, which AGP requires this flag to consume.
|
||||
android.useAndroidX=true
|
||||
# AGP loads many classes into the Gradle daemon's Metaspace; combined with the KMP
|
||||
# matrix, binary-compatibility-validator, Kover and Dokka in a single build, the
|
||||
# default 512m heap / 384m Metaspace is exhausted (daemon OOM). Raise both.
|
||||
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
[versions]
|
||||
kotlin = "2.1.0"
|
||||
agp = "8.7.2"
|
||||
androidCompileSdk = "35"
|
||||
androidMinSdk = "21"
|
||||
activityCompose = "1.9.3"
|
||||
dokka = "2.2.0"
|
||||
mavenPublish = "0.34.0"
|
||||
binaryCompatibilityValidator = "0.16.3"
|
||||
@@ -11,7 +15,10 @@ composeMultiplatform = "1.7.3"
|
||||
|
||||
[plugins]
|
||||
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
|
||||
binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidator" }
|
||||
@@ -24,3 +31,4 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c
|
||||
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
|
||||
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
48
samples-android/build.gradle.kts
Normal file
48
samples-android/build.gradle.kts
Normal file
@@ -0,0 +1,48 @@
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.kotlinAndroid)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
// Match the Kotlin bytecode target to the Java level in android.compileOptions below.
|
||||
compilerOptions {
|
||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.github.adriankuta.treestructure.sample"
|
||||
compileSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.github.adriankuta.treestructure.sample"
|
||||
minSdk = libs.versions.androidMinSdk.get().toInt()
|
||||
targetSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||
versionCode = 1
|
||||
versionName = rootProject.version.toString()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":"))
|
||||
implementation(project(":tree-structure-compose"))
|
||||
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(compose.components.uiToolingPreview)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
debugImplementation(compose.uiTooling)
|
||||
}
|
||||
18
samples-android/src/main/AndroidManifest.xml
Normal file
18
samples-android/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="Tree Sample"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.github.adriankuta.treestructure.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.github.adriankuta.datastructure.tree.TreeNode
|
||||
import com.github.adriankuta.datastructure.tree.compose.LazyTree
|
||||
import com.github.adriankuta.datastructure.tree.compose.TreeNodeRow
|
||||
import com.github.adriankuta.datastructure.tree.tree
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
Surface(modifier = Modifier.fillMaxSize()) {
|
||||
TreeSampleScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Renders the [sampleTree] with the library's default [TreeNodeRow] via the one-line [LazyTree]. */
|
||||
@Composable
|
||||
fun TreeSampleScreen() {
|
||||
LazyTree(sampleTree())
|
||||
}
|
||||
|
||||
private fun sampleTree(): TreeNode<String> = tree("World") {
|
||||
child("North America") {
|
||||
child("USA")
|
||||
child("Canada")
|
||||
}
|
||||
child("Europe") {
|
||||
child("Poland")
|
||||
child("Germany")
|
||||
child("Spain")
|
||||
}
|
||||
child("Asia") {
|
||||
child("Japan")
|
||||
child("India")
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TreeSampleScreenPreview() {
|
||||
MaterialTheme {
|
||||
Surface {
|
||||
TreeSampleScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TreeNodeRowPreview() {
|
||||
MaterialTheme {
|
||||
TreeNodeRow(
|
||||
node = TreeNode("Europe").apply { addChild(TreeNode("Poland")) },
|
||||
depth = 1,
|
||||
expanded = true,
|
||||
toggle = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,11 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "tree-structure"
|
||||
|
||||
include(":tree-structure-serialization")
|
||||
@@ -5,3 +13,4 @@ include(":tree-structure-coroutines")
|
||||
include(":tree-structure-compose")
|
||||
include(":tree-structure-immutable")
|
||||
include(":samples")
|
||||
include(":samples-android")
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
public final class com/github/adriankuta/datastructure/tree/compose/LazyTreeKt {
|
||||
public static final fun LazyTree (Lcom/github/adriankuta/datastructure/tree/TreeNode;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function6;Landroidx/compose/runtime/Composer;II)V
|
||||
public static final fun LazyTree-hGBTI10 (Lcom/github/adriankuta/datastructure/tree/TreeNode;Landroidx/compose/ui/Modifier;ZFLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/compose/TreeNodeRowKt {
|
||||
public static final fun TreeNodeRow-PfoAEA0 (Lcom/github/adriankuta/datastructure/tree/TreeNode;IZLkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;FLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
public final class com/github/adriankuta/datastructure/tree/compose/LazyTreeKt {
|
||||
public static final fun LazyTree (Lcom/github/adriankuta/datastructure/tree/TreeNode;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function6;Landroidx/compose/runtime/Composer;II)V
|
||||
public static final fun LazyTree-hGBTI10 (Lcom/github/adriankuta/datastructure/tree/TreeNode;Landroidx/compose/ui/Modifier;ZFLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
|
||||
}
|
||||
|
||||
public final class com/github/adriankuta/datastructure/tree/compose/TreeNodeRowKt {
|
||||
public static final fun TreeNodeRow-PfoAEA0 (Lcom/github/adriankuta/datastructure/tree/TreeNode;IZLkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;FLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
public final class com/github/adriankuta/datastructure/tree/compose/LazyTreeKt {
|
||||
public static final fun LazyTree (Lcom/github/adriankuta/datastructure/tree/TreeNode;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function6;Landroidx/compose/runtime/Composer;II)V
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
alias(libs.plugins.dokka)
|
||||
@@ -68,6 +69,14 @@ kotlin {
|
||||
|
||||
jvm()
|
||||
|
||||
androidTarget {
|
||||
publishLibraryVariants("release")
|
||||
// Match the Android variant's Kotlin bytecode to the Java level set in compileOptions.
|
||||
compilerOptions {
|
||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalWasmDsl::class)
|
||||
wasmJs {
|
||||
browser()
|
||||
@@ -85,3 +94,15 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.github.adriankuta.datastructure.tree.compose"
|
||||
compileSdk = libs.versions.androidCompileSdk.get().toInt()
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.androidMinSdk.get().toInt()
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.adriankuta.datastructure.tree.TreeNode
|
||||
|
||||
/**
|
||||
@@ -49,6 +51,35 @@ public fun <T> LazyTree(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience overload of [LazyTree] that renders each node with the built-in [TreeNodeRow], so the
|
||||
* common case is a single call:
|
||||
*
|
||||
* ```
|
||||
* LazyTree(root)
|
||||
* ```
|
||||
*
|
||||
* Use the overload that takes a `nodeContent` lambda when you need full control over a node's look.
|
||||
*
|
||||
* @param root the root of the tree to display.
|
||||
* @param modifier the [Modifier] applied to the underlying [LazyColumn].
|
||||
* @param initiallyExpanded whether nodes start expanded.
|
||||
* @param indent the horizontal indentation applied per depth level.
|
||||
* @param label maps a node's value to the text shown. Defaults to `toString()`.
|
||||
*/
|
||||
@Composable
|
||||
public fun <T> LazyTree(
|
||||
root: TreeNode<T>,
|
||||
modifier: Modifier = Modifier,
|
||||
initiallyExpanded: Boolean = true,
|
||||
indent: Dp = 16.dp,
|
||||
label: (T) -> String = { it.toString() },
|
||||
) {
|
||||
LazyTree(root, modifier, initiallyExpanded) { node, depth, expanded, toggle ->
|
||||
TreeNodeRow(node, depth, expanded, toggle, indent = indent, label = label)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens the tree into the list of currently-visible `(node, depth)` pairs in pre-order, skipping
|
||||
* the subtrees of collapsed nodes. Iterative, so it is safe on deep trees.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.github.adriankuta.datastructure.tree.compose
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.adriankuta.datastructure.tree.TreeNode
|
||||
|
||||
/**
|
||||
* A sensible default row for a single node in a [LazyTree]. The whole row is clickable to expand or
|
||||
* collapse, indentation reflects [depth], and a `▾`/`▸` marker precedes non-leaf nodes.
|
||||
*
|
||||
* It is intentionally foundation-only (no Material), so using it does not pull a theming dependency
|
||||
* into your app. For full control over a node's appearance, use the `LazyTree` overload that takes a
|
||||
* `nodeContent` lambda instead.
|
||||
*
|
||||
* ```
|
||||
* LazyTree(root) { node, depth, expanded, toggle ->
|
||||
* TreeNodeRow(node, depth, expanded, toggle)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param node the node to render.
|
||||
* @param depth the node's depth in the tree (root = 0), used for indentation.
|
||||
* @param expanded whether the node is currently expanded.
|
||||
* @param toggle flips this node's expansion state; invoked when the row is clicked.
|
||||
* @param modifier the [Modifier] applied to the row.
|
||||
* @param indent the horizontal indentation applied per depth level.
|
||||
* @param label maps the node's value to the text shown. Defaults to `toString()`.
|
||||
*/
|
||||
@Composable
|
||||
public fun <T> TreeNodeRow(
|
||||
node: TreeNode<T>,
|
||||
depth: Int,
|
||||
expanded: Boolean,
|
||||
toggle: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
indent: Dp = 16.dp,
|
||||
label: (T) -> String = { it.toString() },
|
||||
) {
|
||||
val marker = when {
|
||||
node.children.isEmpty() -> ""
|
||||
expanded -> "▾ "
|
||||
else -> "▸ "
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = toggle)
|
||||
.padding(start = indent * depth, top = 8.dp, bottom = 8.dp, end = 8.dp),
|
||||
) {
|
||||
BasicText(text = marker + label(node.value))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user