Compose-HeatMap
1.0.5indexedCreate GitHub-style heatmaps using Jetpack Compose for visualizing time-based data patterns. Features include easy setup, high customization, interactive elements, and generic data support.
Create GitHub-style heatmaps using Jetpack Compose for visualizing time-based data patterns. Features include easy setup, high customization, interactive elements, and generic data support.
Effortlessly create GitHub-style heatmaps in Jetpack Compose—perfect for visualizing a variety of time-based data patterns.
This project is based on kotlinx-datetime for LocalDate-related operations, not java.time.*!
refer to kotlinx-datetime to add dep.
build.gradle.kts
implementation("com.fleeys:heatmap:1.0.5")
build.gradle
implementation 'com.fleeys:heatmap:1.0.5'
// ../library/src/commonMain/kotlin/com/fleeys/heatmap/HeatMap.kt
@Composable
fun <T> HeatMap(
modifier: Modifier = Modifier,
data: List<Heat<T>>,
style: HeatMapStyle = HeatMapStyle(),
scrollState: LazyListState = rememberLazyListState(),
onScrolledToTop: (() -> )? = ,
onScrolledToBottom: (() -> )? = ,
onHeatClick: (Heat<T>) -> ,
)
[!IMPORTANT]
The project is in the experimental phase. All APIs can change incompatibly or be dropped without the deprecation cycle!
Works right out of the box without much setup.
Almost everything you see can be manipulated.
// ../library/src/commonMain/kotlin/com/fleeys/heatmap/style/HeatMapStyle.kt
@Immutable
data class HeatMapStyle(
heatStyle: HeatStyle = HeatStyle(),
labelStyle: LabelStyle = LabelStyle(),
heatMapPadding: PaddingValues = PaddingValues(dp),
startFromEnd: =
)
CustomHeatMapStyle = HeatMapStyle().copy(
heatStyle = HeatStyle().copy(
heatColor = HeatColor().copy(
activeLowestColor = Color(),
activeHighestColor = Color(),
),
heatShape = CircleShape,
),
startFromEnd =
)
Not just present, but interact.
The operation is richer.
// ../library/src/commonMain/kotlin/com/fleeys/heatmap/HeatMap.kt
import kotlinx.datetime.LocalDate
data class Heat<T>(
val date: LocalDate,
var value: Double,
var data: T? = null
)
This project is built on Compose in an attempt to adapt to the Compose Multiplatform.
The currently adapted platforms are listed below:
Feel free to submit an issue if you have any feedback or suggestions!
The project is quite happy to receive your contributions, and it will be much more robust with your help!
I hope you like it, and if you think it's good, feel free to give a :star: !
Copyright (c) 2024-present. Fleey
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
| Platform | Default Style | Custom Style |
|---|
| Android | ![]() | ![]() |
| Desktop | ![]() | ![]() |
| WasmJs | ![]() | ![]() |
// ../sample/src/commonMain/com/fleeys/heatmap/sample/SampleHeatMap.kt
fun SampleHeatMap() {
val scrollState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
var heatMapStyle by remember { mutableStateOf<HeatMapStyle?>(null) }
val toggleStyle = { heatMapStyle = if (heatMapStyle == null) CustomHeatMapStyle else null }
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.padding(16.dp)
) {
HeatMap(
data = generateHeats(),
scrollState = scrollState,
style = heatMapStyle ?: HeatMapStyle(),
onScrolledToTop = { println("Scrolled to Top") },
onScrolledToBottom = { println("Scrolled to Bottom") }
) { println("Clicked: $it") }
Column(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(
onClick = toggleStyle,
) {
Text("Toggle Style")
}
Button(onClick = {
coroutineScope.launch {
scrollState.animateScrollToItem(0)
}
}) {
Text("Scroll to Top")
}
Button(onClick = {
coroutineScope.launch {
scrollState.animateScrollToItem(scrollState.layoutInfo.totalItemsCount - 1)
}
}) {
Text("Scroll to Bottom")
}
}
}
}
// fake data
private fun generateHeats(): List<Heat<Unit>> {
val startDate = LocalDate(2022, 11, 11)
val curDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
return generateSequence(startDate) { date ->
if (date < curDate) date + DatePeriod(days = 1) else null
}.map { date ->
val value = Random.nextDouble(0.0, 32.0)
Heat<Unit>(date, value)
}.toList()
}
| Platform | State | Sample | Note |
|---|
| Android | ✅ | Android-Sample | |
| Desktop | ✅ | Desktop-Sample | |
| WasmJs | :expressionless: | ... | (1) |
Surfaced from shared tags and platforms — no rankings paid for.