Last active
November 25, 2022 18:30
-
-
Save avwie/9b0ee5f2882e8d26517f4ab7fc2b0408 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.avwie.ui.demos.sidebar | |
import androidx.compose.animation.* | |
import androidx.compose.animation.core.animateFloatAsState | |
import androidx.compose.animation.core.spring | |
import androidx.compose.animation.core.tween | |
import androidx.compose.foundation.Image | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.hoverable | |
import androidx.compose.foundation.interaction.MutableInteractionSource | |
import androidx.compose.foundation.interaction.collectIsHoveredAsState | |
import androidx.compose.foundation.layout.* | |
import androidx.compose.foundation.shape.RoundedCornerShape | |
import androidx.compose.material.Text | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.* | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.ExperimentalComposeUiApi | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.alpha | |
import androidx.compose.ui.draw.clip | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.graphics.painter.Painter | |
import androidx.compose.ui.input.pointer.PointerIconDefaults | |
import androidx.compose.ui.input.pointer.pointerHoverIcon | |
import androidx.compose.ui.layout.boundsInParent | |
import androidx.compose.ui.layout.onGloballyPositioned | |
import androidx.compose.ui.platform.LocalDensity | |
import androidx.compose.ui.unit.* | |
import androidx.compose.ui.window.Popup | |
import androidx.compose.ui.window.PopupPositionProvider | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.flow.MutableStateFlow | |
import kotlinx.coroutines.flow.update | |
@Composable fun Sidebar( | |
expanded: Boolean = true, | |
content: @Composable SidebarScope.() -> Unit = {} | |
) { | |
Column ( | |
verticalArrangement = Arrangement.spacedBy(Dimensions.DefaultPadding / 2), | |
modifier = Modifier | |
.doubleBorder() | |
.background(Colors.Sidebar.Background) | |
.padding(Dimensions.DefaultPadding) | |
) { | |
val scope = remember(expanded) { | |
SidebarScope(expanded,this) | |
} | |
content(scope) | |
} | |
} | |
class SidebarScope( | |
val expanded: Boolean, | |
val columnScope: ColumnScope | |
): ColumnScope by columnScope { | |
private var sizes by mutableStateOf(mapOf<Any, Dp>()) | |
private val largestSize by derivedStateOf { sizes.values.maxOrNull() } | |
private fun notifyWidth(key: Any, width: Dp) { | |
sizes += (key to width) | |
} | |
@OptIn(ExperimentalComposeUiApi::class, ExperimentalAnimationApi::class) | |
@Composable fun Item( | |
painter: Painter, | |
title: String, | |
isActive: Boolean = false | |
) { | |
val interactionSource = remember { MutableInteractionSource() } | |
val isHover by interactionSource.collectIsHoveredAsState() | |
val animatedBackgroundColor by animateColorAsState( | |
when { | |
isActive -> Colors.Sidebar.Item.Background.Active | |
isHover -> Colors.Sidebar.Item.Background.Hover | |
else -> Colors.Sidebar.Item.Background.Default | |
} | |
) | |
val widthModifier = remember(largestSize, expanded) { | |
when { | |
expanded && largestSize != null -> Modifier.defaultMinSize(minWidth = largestSize!!) | |
else -> Modifier | |
} | |
} | |
with (LocalDensity.current) { | |
Box( | |
modifier = Modifier | |
.onGloballyPositioned { coords -> | |
this@SidebarScope.notifyWidth(title, coords.boundsInParent().width.toDp()) | |
} | |
.then(widthModifier) | |
.clip(RoundedCornerShape(Dimensions.DefaultCornerRadius / 2)) | |
.background(animatedBackgroundColor) | |
.pointerHoverIcon(PointerIconDefaults.Hand) | |
.hoverable(interactionSource) | |
.padding(Dimensions.DefaultPadding) | |
) { | |
Row( | |
verticalAlignment = Alignment.CenterVertically, | |
) { | |
Image( | |
painter = painter, | |
contentDescription = title, | |
modifier = Modifier.size(Dimensions.Sidebar.ItemSize) | |
) | |
AnimatedContent( | |
targetState = this@SidebarScope.expanded, | |
) { targetExpanded -> | |
if (targetExpanded) { | |
Text( | |
text = title, | |
modifier = Modifier | |
.padding(horizontal = Dimensions.DefaultPadding) | |
) | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment