|
@@ -0,0 +1,134 @@
|
|
|
+package it.vfsfitvnm.vimusic.ui.components.themed
|
|
|
+
|
|
|
+import androidx.compose.animation.animateColor
|
|
|
+import androidx.compose.animation.core.animateFloat
|
|
|
+import androidx.compose.animation.core.updateTransition
|
|
|
+import androidx.compose.foundation.Image
|
|
|
+import androidx.compose.foundation.clickable
|
|
|
+import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
|
+import androidx.compose.foundation.layout.Box
|
|
|
+import androidx.compose.foundation.layout.Column
|
|
|
+import androidx.compose.foundation.layout.ColumnScope
|
|
|
+import androidx.compose.foundation.layout.Row
|
|
|
+import androidx.compose.foundation.layout.offset
|
|
|
+import androidx.compose.foundation.layout.padding
|
|
|
+import androidx.compose.foundation.layout.size
|
|
|
+import androidx.compose.foundation.rememberScrollState
|
|
|
+import androidx.compose.foundation.shape.CircleShape
|
|
|
+import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
+import androidx.compose.foundation.text.BasicText
|
|
|
+import androidx.compose.foundation.verticalScroll
|
|
|
+import androidx.compose.material.ripple.rememberRipple
|
|
|
+import androidx.compose.runtime.Composable
|
|
|
+import androidx.compose.runtime.getValue
|
|
|
+import androidx.compose.runtime.remember
|
|
|
+import androidx.compose.ui.Alignment
|
|
|
+import androidx.compose.ui.Modifier
|
|
|
+import androidx.compose.ui.draw.clip
|
|
|
+import androidx.compose.ui.draw.rotate
|
|
|
+import androidx.compose.ui.graphics.ColorFilter
|
|
|
+import androidx.compose.ui.graphics.graphicsLayer
|
|
|
+import androidx.compose.ui.layout.layout
|
|
|
+import androidx.compose.ui.res.painterResource
|
|
|
+import androidx.compose.ui.unit.dp
|
|
|
+import it.vfsfitvnm.vimusic.ui.styling.Dimensions
|
|
|
+import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
|
|
+import it.vfsfitvnm.vimusic.utils.semiBold
|
|
|
+
|
|
|
+@Composable
|
|
|
+fun NavigationRail(
|
|
|
+ topIconButtonId: Int,
|
|
|
+ onTopIconButtonClick: () -> Unit,
|
|
|
+ tabIndex: Int,
|
|
|
+ onTabIndexChanged: (Int) -> Unit,
|
|
|
+ content: @Composable ColumnScope.(@Composable (Int, String, Int) -> Unit) -> Unit,
|
|
|
+ modifier: Modifier = Modifier
|
|
|
+) {
|
|
|
+ val (colorPalette, typography) = LocalAppearance.current
|
|
|
+
|
|
|
+ Column(
|
|
|
+ horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
+ modifier = modifier
|
|
|
+ ) {
|
|
|
+ Box(
|
|
|
+ contentAlignment = Alignment.TopCenter,
|
|
|
+ modifier = Modifier
|
|
|
+ .size(width = Dimensions.navigationRailWidth, height = Dimensions.headerHeight)
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painter = painterResource(topIconButtonId),
|
|
|
+ contentDescription = null,
|
|
|
+ colorFilter = ColorFilter.tint(colorPalette.textSecondary),
|
|
|
+ modifier = Modifier
|
|
|
+ .offset(x = Dimensions.navigationRailIconOffset, y = 48.dp)
|
|
|
+ .clip(CircleShape)
|
|
|
+ .clickable(onClick = onTopIconButtonClick)
|
|
|
+ .padding(all = 12.dp)
|
|
|
+ .size(22.dp)
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ Column(
|
|
|
+ modifier = modifier
|
|
|
+ .verticalScroll(rememberScrollState())
|
|
|
+ ) {
|
|
|
+ val transition = updateTransition(targetState = tabIndex, label = null)
|
|
|
+
|
|
|
+ content { index, text, icon ->
|
|
|
+ val dothAlpha by transition.animateFloat(label = "") {
|
|
|
+ if (it == index) 1f else 0f
|
|
|
+ }
|
|
|
+
|
|
|
+ val textColor by transition.animateColor(label = "") {
|
|
|
+ if (it == index) colorPalette.text else colorPalette.textDisabled
|
|
|
+ }
|
|
|
+
|
|
|
+ Row(
|
|
|
+ verticalAlignment = Alignment.CenterVertically,
|
|
|
+ modifier = Modifier
|
|
|
+ .clip(RoundedCornerShape(24.dp))
|
|
|
+ .clickable(
|
|
|
+ indication = rememberRipple(bounded = true),
|
|
|
+ interactionSource = remember { MutableInteractionSource() },
|
|
|
+ onClick = { onTabIndexChanged(index) }
|
|
|
+ )
|
|
|
+ .padding(horizontal = 8.dp)
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painter = painterResource(icon),
|
|
|
+ contentDescription = null,
|
|
|
+ colorFilter = ColorFilter.tint(colorPalette.text),
|
|
|
+ modifier = Modifier
|
|
|
+ .vertical()
|
|
|
+ .graphicsLayer {
|
|
|
+ alpha = dothAlpha
|
|
|
+ translationX = (1f - dothAlpha) * -48.dp.toPx()
|
|
|
+ rotationZ = -90f
|
|
|
+ }
|
|
|
+ .size(Dimensions.navigationRailIconOffset * 2)
|
|
|
+ )
|
|
|
+
|
|
|
+ BasicText(
|
|
|
+ text = text,
|
|
|
+ style = typography.xs.semiBold.copy(color = textColor),
|
|
|
+ modifier = Modifier
|
|
|
+ .vertical()
|
|
|
+ .rotate(-90f)
|
|
|
+ .padding(horizontal = 16.dp)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private fun Modifier.vertical() =
|
|
|
+ layout { measurable, constraints ->
|
|
|
+ val placeable = measurable.measure(constraints)
|
|
|
+ layout(placeable.height, placeable.width) {
|
|
|
+ placeable.place(
|
|
|
+ x = -(placeable.width / 2 - placeable.height / 2),
|
|
|
+ y = -(placeable.height / 2 - placeable.width / 2)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|