Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions design/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,60 @@ package com.urlaunched.android.design.ui.slider {

}

package com.urlaunched.android.design.ui.tabsrow {

public final class AnimatedTabsRowKt {
method @androidx.compose.runtime.Composable public static void AnimatedTabsRow(optional androidx.compose.ui.Modifier modifier, com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState synchronizedLazyGridScrollState, androidx.compose.foundation.pager.PagerState pagerState, kotlin.jvm.functions.Function0<kotlin.Unit> topBarContent, java.util.List<java.lang.String> pages, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional float tabsHeight, optional float indicatorHeight, optional androidx.compose.ui.unit.Dp? indicatorShadow, optional boolean enabled, optional java.util.List<androidx.compose.ui.graphics.Color> colors, optional long selectedTextColor, optional long unselectedTextColor, optional long containerColor, optional androidx.compose.ui.text.TextStyle selectedTextStyle, optional androidx.compose.ui.text.TextStyle unselectedTextStyle, optional long minFontSize, optional long backgroundColor, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onTabChange, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit>? indicator, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? startContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? endContent);
}

public final class AnimatedTabsRowScaffoldKt {
method @androidx.compose.runtime.Composable public static void AnimatedTabsRowScaffold(optional androidx.compose.ui.Modifier modifier, com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState synchronizedLazyGridScrollState, androidx.compose.foundation.pager.PagerState pagerState, java.util.List<java.lang.String> pages, optional androidx.compose.foundation.layout.PaddingValues tabsPadding, optional float tabsHeight, optional float indicatorHeight, optional androidx.compose.ui.unit.Dp? indicatorShadow, optional boolean enabled, optional java.util.List<androidx.compose.ui.graphics.Color> colors, optional long selectedTextColor, optional long unselectedTextColor, optional long containerColor, optional androidx.compose.ui.text.TextStyle selectedTextStyle, optional androidx.compose.ui.text.TextStyle unselectedTextStyle, optional long minFontSize, optional long backgroundColor, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onTabChange, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit>? tabIndicator, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? tabStartContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? tabEndContent, kotlin.jvm.functions.Function0<kotlin.Unit> topBarContent, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}

public final class TabsRowKt {
method @androidx.compose.runtime.Composable public static void TabsRow(optional androidx.compose.ui.Modifier modifier, java.util.List<java.lang.String> pages, androidx.compose.foundation.pager.PagerState pagerState, optional float tabsHeight, optional float indicatorHeight, optional androidx.compose.ui.unit.Dp? indicatorShadow, optional boolean enabled, optional java.util.List<androidx.compose.ui.graphics.Color> colors, optional long selectedTextColor, optional long unselectedTextColor, optional long containerColor, optional androidx.compose.ui.text.TextStyle selectedTextStyle, optional androidx.compose.ui.text.TextStyle unselectedTextStyle, optional long minFontSize, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> onTabChange, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit>? indicator, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? startContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? endContent);
}

}

package com.urlaunched.android.design.ui.tabsrow.components {

public final class RememberScrollProgressKt {
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> rememberScrollProgress(androidx.compose.foundation.lazy.grid.LazyGridState, float targetOffset);
method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.lang.Float> rememberScrollProgress(androidx.compose.foundation.lazy.LazyListState, float targetOffset);
}

public final class SynchronizedLazyGridScrollState {
ctor public SynchronizedLazyGridScrollState(androidx.compose.foundation.lazy.grid.LazyGridState firstTabState, androidx.compose.foundation.lazy.grid.LazyGridState secondTabState, int targetOffset, int firstItemOffsetPx);
method public androidx.compose.foundation.lazy.grid.LazyGridState component1();
method public androidx.compose.foundation.lazy.grid.LazyGridState component2();
method public int component3();
method public com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState copy(androidx.compose.foundation.lazy.grid.LazyGridState firstTabState, androidx.compose.foundation.lazy.grid.LazyGridState secondTabState, int targetOffset, int firstItemOffsetPx);
method public androidx.compose.foundation.lazy.grid.LazyGridState getFirstTabState();
method public float getFirstTabStateProgress();
method public androidx.compose.foundation.lazy.grid.LazyGridState getSecondTabState();
method public float getSecondTabStateProgress();
method public int getTargetOffset();
method public suspend Object? scrollToTopFirstTab(optional boolean animated, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public suspend Object? scrollToTopSecondTab(optional boolean animated, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
property public final androidx.compose.foundation.lazy.grid.LazyGridState firstTabState;
property public final float firstTabStateProgress;
property public final androidx.compose.foundation.lazy.grid.LazyGridState secondTabState;
property public final float secondTabStateProgress;
property public final int targetOffset;
}

public final class SynchronizedLazyGridScrollStateKt {
method @androidx.compose.runtime.Composable public static com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState rememberSynchronizedLazyGridScrollStates(optional float spacing, optional float initialOffset);
}

public final class VerticalOffsetKt {
method @androidx.compose.runtime.Composable public static androidx.compose.ui.Modifier verticalOffset(androidx.compose.ui.Modifier, androidx.compose.foundation.lazy.grid.LazyGridState firstScrollState, androidx.compose.foundation.lazy.grid.LazyGridState secondScrollState, float initialOffset, float maxOffset);
method @androidx.compose.runtime.Composable public static androidx.compose.ui.Modifier verticalOffset(androidx.compose.ui.Modifier, androidx.compose.foundation.lazy.LazyListState firstScrollState, androidx.compose.foundation.lazy.LazyListState secondScrollState, float initialOffset, float maxOffset);
}

}

package com.urlaunched.android.design.ui.textfield {

public final class LocalTextFieldConfigsKt {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.urlaunched.android.design.ui.tabsrow

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
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.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import com.urlaunched.android.design.resources.dimens.Dimens
import com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState
import com.urlaunched.android.design.ui.tabsrow.components.rememberScrollProgress
import com.urlaunched.android.design.ui.tabsrow.components.rememberSynchronizedLazyGridScrollStates
import com.urlaunched.android.design.ui.tabsrow.components.verticalOffset
import com.urlaunched.android.design.ui.tabsrow.constants.TabsRowDimens
import kotlin.math.max

@Composable
fun AnimatedTabsRow(
modifier: Modifier = Modifier,
synchronizedLazyGridScrollState: SynchronizedLazyGridScrollState,
pagerState: PagerState,
topBarContent: @Composable () -> Unit,
pages: List<String>,
contentPadding: PaddingValues = PaddingValues(),
tabsHeight: Dp = TabsRowDimens.pagerTabsHeight,
indicatorHeight: Dp = TabsRowDimens.pagerTabsIndicatorHeight,
indicatorShadow: Dp? = null,
enabled: Boolean = true,
colors: List<Color> = listOf(Color.DarkGray),
selectedTextColor: Color = Color.White,
unselectedTextColor: Color = Color.Gray,
containerColor: Color = Color.White,
selectedTextStyle: TextStyle = Typography().titleSmall,
unselectedTextStyle: TextStyle = Typography().labelLarge,
minFontSize: TextUnit = Typography().bodyMedium.fontSize,
backgroundColor: Color = Color.White,
onTabChange: (index: Int) -> Unit = {},
indicator: @Composable ((page: Int) -> Unit)? = null,
startContent: @Composable (RowScope.() -> Unit)? = null,
endContent: @Composable (RowScope.() -> Unit)? = null
) {
val firstTabScrollProgress by synchronizedLazyGridScrollState.firstTabState.rememberScrollProgress(Dimens.spacingNormalSpecial)
val secondTabScrollProgress by synchronizedLazyGridScrollState.secondTabState.rememberScrollProgress(Dimens.spacingNormalSpecial)
val progress by remember { derivedStateOf { max(firstTabScrollProgress, secondTabScrollProgress) } }

Box(modifier = modifier) {
Box(
modifier = Modifier
.matchParentSize()
.verticalOffset(
firstScrollState = synchronizedLazyGridScrollState.firstTabState,
secondScrollState = synchronizedLazyGridScrollState.secondTabState,
initialOffset = Dimens.spacingNormal,
maxOffset = Dimens.zeroDp
)
.clip(
RoundedCornerShape(
bottomStart = Dimens.cornerRadiusNormal,
bottomEnd = Dimens.cornerRadiusNormal
)
)
.alpha(progress)
.background(backgroundColor)
)

Column {
topBarContent()

TabsRow(
modifier = Modifier
.padding(contentPadding)
.verticalOffset(
firstScrollState = synchronizedLazyGridScrollState.firstTabState,
secondScrollState = synchronizedLazyGridScrollState.secondTabState,
initialOffset = Dimens.spacingSmall,
maxOffset = Dimens.spacingSmall
),
pages = pages,
containerColor = lerp(
start = backgroundColor,
stop = containerColor,
fraction = progress
),
pagerState = pagerState,
tabsHeight = tabsHeight,
indicatorHeight = indicatorHeight,
indicatorShadow = indicatorShadow,
enabled = enabled,
colors = colors,
selectedTextColor = selectedTextColor,
unselectedTextColor = unselectedTextColor,
selectedTextStyle = selectedTextStyle,
unselectedTextStyle = unselectedTextStyle,
onTabChange = onTabChange,
minFontSize = minFontSize,
indicator = indicator,
startContent = startContent,
endContent = endContent
)
}
}
}

@Preview(showBackground = true, backgroundColor = 0xff999999)
@Composable
private fun AnimatedTabRowPreview() {
val pagerState = rememberPagerState(pageCount = { 2 })

Box(modifier = Modifier.padding(bottom = Dimens.spacingBig)) {
AnimatedTabsRow(
modifier = Modifier.padding(horizontal = Dimens.spacingNormal),
synchronizedLazyGridScrollState = rememberSynchronizedLazyGridScrollStates(
spacing = Dimens.spacingNormalSpecial,
initialOffset = Dimens.spacingNormal
),
pagerState = pagerState,
pages = listOf("First", "Second"),
containerColor = Color.White,
topBarContent = {
Box(
modifier = Modifier.height(Dimens.spacingLarge),
contentAlignment = Alignment.Center
) {
Text("Top Bar")
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.urlaunched.android.design.ui.tabsrow

import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import com.urlaunched.android.design.ui.tabsrow.components.SynchronizedLazyGridScrollState
import com.urlaunched.android.design.ui.tabsrow.constants.TabsRowDimens

@Composable
fun AnimatedTabsRowScaffold(
modifier: Modifier = Modifier,
synchronizedLazyGridScrollState: SynchronizedLazyGridScrollState,
pagerState: PagerState,
pages: List<String>,
tabsPadding: PaddingValues = PaddingValues(),
tabsHeight: Dp = TabsRowDimens.pagerTabsHeight,
indicatorHeight: Dp = TabsRowDimens.pagerTabsIndicatorHeight,
indicatorShadow: Dp? = null,
enabled: Boolean = true,
colors: List<Color> = listOf(Color.DarkGray),
selectedTextColor: Color = Color.White,
unselectedTextColor: Color = Color.Gray,
containerColor: Color = Color.White,
selectedTextStyle: TextStyle = Typography().titleSmall,
unselectedTextStyle: TextStyle = Typography().labelLarge,
minFontSize: TextUnit = Typography().bodyMedium.fontSize,
backgroundColor: Color = Color.White,
onTabChange: (index: Int) -> Unit = {},
tabIndicator: @Composable ((page: Int) -> Unit)? = null,
tabStartContent: @Composable (RowScope.() -> Unit)? = null,
tabEndContent: @Composable (RowScope.() -> Unit)? = null,
topBarContent: @Composable () -> Unit,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = {
AnimatedTabsRow(
synchronizedLazyGridScrollState = synchronizedLazyGridScrollState,
pagerState = pagerState,
pages = pages,
topBarContent = topBarContent,
contentPadding = tabsPadding,
tabsHeight = tabsHeight,
indicatorHeight = indicatorHeight,
indicatorShadow = indicatorShadow,
enabled = enabled,
colors = colors,
selectedTextColor = selectedTextColor,
unselectedTextColor = unselectedTextColor,
containerColor = containerColor,
backgroundColor = backgroundColor,
selectedTextStyle = selectedTextStyle,
unselectedTextStyle = unselectedTextStyle,
minFontSize = minFontSize,
onTabChange = onTabChange,
indicator = tabIndicator,
startContent = tabStartContent,
endContent = tabEndContent
)

CompositionLocalProvider(LocalOverscrollFactory provides null) {
content()
}
}
) { measurables, constraints ->
val offset = synchronizedLazyGridScrollState.targetOffset
val topBarPlaceable = measurables[0].measure(constraints)
val contentPlaceable = measurables[1].measure(
constraints.copy(maxHeight = constraints.maxHeight - topBarPlaceable.height + offset)
)

layout(width = constraints.maxWidth, height = constraints.maxHeight) {
contentPlaceable.placeRelative(
x = 0,
y = topBarPlaceable.height - offset
)

topBarPlaceable.placeRelative(
x = 0,
y = 0
)
}
}
}
Loading