V2 Progressbar (#285)
* Adding progressbar activity, determinate progressbars * indeterminate linear progress bar * adding indeterminate progress abrs * Adding fading animation for ciruclar progress * adding Shimmer effect * review changes * shimmer width and height * seperated circular, linear and shimmer impl as well as tokens * adding neutral style for circular progress * review changes -2 * moving shimmer to shimmer package Co-authored-by: PraveenKumar Yeruva <pyeruva@microsoft.com>
This commit is contained in:
Родитель
7e9b2ccf06
Коммит
68c8d4f136
|
@ -61,6 +61,7 @@
|
|||
<activity android:name="com.microsoft.fluentuidemo.demos.V2PersonaActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.PopupMenuActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.ProgressActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.V2ProgressActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.SnackbarActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.V2SegmentedControlActivity" />
|
||||
<activity android:name="com.microsoft.fluentuidemo.demos.TabLayoutActivity" />
|
||||
|
|
|
@ -39,6 +39,7 @@ const val PERSONA_VIEW = "PersonaView"
|
|||
const val V2PERSONA = "V2 Persona"
|
||||
const val POPUP_MENU = "PopupMenu"
|
||||
const val PROGRESS = "Progress"
|
||||
const val V2PROGRESS = "V2 Progress"
|
||||
const val SNACKBAR = "Snackbar"
|
||||
const val V2SEGMENTED_CONTROL = "V2 SegmentedControl"
|
||||
const val TAB_LAYOUT = "TabLayout"
|
||||
|
@ -77,6 +78,7 @@ val DEMOS = arrayListOf(
|
|||
Demo(V2PERSONA, V2PersonaActivity::class),
|
||||
Demo(POPUP_MENU, PopupMenuActivity::class),
|
||||
Demo(PROGRESS, ProgressActivity::class),
|
||||
Demo(V2PROGRESS, V2ProgressActivity::class),
|
||||
Demo(SNACKBAR, SnackbarActivity::class),
|
||||
Demo(V2SEGMENTED_CONTROL, V2SegmentedControlActivity::class),
|
||||
Demo(TAB_LAYOUT, TabLayoutActivity::class),
|
||||
|
|
|
@ -0,0 +1,359 @@
|
|||
package com.microsoft.fluentuidemo.demos
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.AliasTokens
|
||||
import com.microsoft.fluentui.theme.token.FluentStyle
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorSize
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.ShimmerShape
|
||||
import com.microsoft.fluentui.tokenized.progress.CircularProgressIndicator
|
||||
import com.microsoft.fluentui.tokenized.progress.LinearProgressIndicator
|
||||
import com.microsoft.fluentui.tokenized.progress.Shimmer
|
||||
import com.microsoft.fluentuidemo.DemoActivity
|
||||
import com.microsoft.fluentuidemo.R
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.random.Random
|
||||
|
||||
class V2ProgressActivity : DemoActivity() {
|
||||
override val contentLayoutId: Int
|
||||
get() = R.layout.v2_activity_compose
|
||||
override val contentNeedsScrollableContainer: Boolean
|
||||
get() = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val compose_here = findViewById<ComposeView>(R.id.compose_here)
|
||||
|
||||
compose_here.setContent {
|
||||
FluentTheme {
|
||||
createActivityUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun createActivityUI() {
|
||||
var linearProgress by remember { mutableStateOf(0f) }
|
||||
var circularProgress by remember { mutableStateOf(0f) }
|
||||
val textColor =
|
||||
FluentTheme.aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
val brandTextColor =
|
||||
FluentTheme.aliasTokens.brandForegroundColor[AliasTokens.BrandForegroundColorTokens.BrandForeground1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
Column(
|
||||
Modifier
|
||||
.padding(start = 12.dp, top = 12.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
LinearProgressBarExample(brandTextColor = brandTextColor, textColor = textColor)
|
||||
CircularProgressBarExamples(textColor = textColor)
|
||||
DeterminateProgressbarExamples(
|
||||
brandTextColor = brandTextColor,
|
||||
textColor = textColor,
|
||||
linearProgress,
|
||||
circularProgress
|
||||
)
|
||||
IndeterminateProgressBarExamples(brandTextColor = brandTextColor, textColor = textColor)
|
||||
shimmerExamples(brandTextColor = brandTextColor, textColor = textColor)
|
||||
}
|
||||
LaunchedEffect(key1 = linearProgress) {
|
||||
if (linearProgress >= 1.0) {
|
||||
linearProgress = 1f
|
||||
delay(1000)
|
||||
linearProgress = 0f
|
||||
} else {
|
||||
delay(500)
|
||||
linearProgress += Random.nextFloat() / 5
|
||||
}
|
||||
}
|
||||
LaunchedEffect(key1 = circularProgress) {
|
||||
if (circularProgress >= 1.0) {
|
||||
circularProgress = 1f
|
||||
delay(1000)
|
||||
circularProgress = 0f
|
||||
} else {
|
||||
delay(500)
|
||||
circularProgress += Random.nextFloat() / 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LinearProgressBarExample(brandTextColor: Color, textColor: Color) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "ProgressBars",
|
||||
color = brandTextColor,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "XXXSmall - 2dp",
|
||||
color = textColor
|
||||
)
|
||||
LinearProgressIndicator(modifier = Modifier.width(240.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CircularProgressBarExamples(textColor: Color) {
|
||||
Column {
|
||||
Row(
|
||||
Modifier.height(42.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "XSmall - 12dp", modifier = Modifier.width(100.dp),
|
||||
color = textColor
|
||||
)
|
||||
CircularProgressIndicator(style = FluentStyle.Brand)
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
Row(
|
||||
Modifier.height(42.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Small - 16dp", modifier = Modifier.width(100.dp),
|
||||
color = textColor
|
||||
)
|
||||
CircularProgressIndicator(size = CircularProgressIndicatorSize.XSmall, style = FluentStyle.Brand)
|
||||
CircularProgressIndicator(
|
||||
CircularProgressIndicatorSize.XSmall
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier.height(42.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Medium - 24dp", modifier = Modifier.width(100.dp),
|
||||
color = textColor
|
||||
)
|
||||
CircularProgressIndicator(size = CircularProgressIndicatorSize.Medium, style = FluentStyle.Brand)
|
||||
CircularProgressIndicator(
|
||||
CircularProgressIndicatorSize.Medium
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier.height(48.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Large - 32dp", modifier = Modifier.width(100.dp),
|
||||
color = textColor
|
||||
)
|
||||
CircularProgressIndicator(size = CircularProgressIndicatorSize.Large, style = FluentStyle.Brand)
|
||||
CircularProgressIndicator(
|
||||
CircularProgressIndicatorSize.Large
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier.height(64.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "XLarge - 36dp", modifier = Modifier.width(100.dp),
|
||||
color = textColor
|
||||
)
|
||||
CircularProgressIndicator(CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
|
||||
CircularProgressIndicator(
|
||||
CircularProgressIndicatorSize.XLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeterminateProgressbarExamples(
|
||||
brandTextColor: Color,
|
||||
textColor: Color,
|
||||
linearProgress: Float,
|
||||
circularProgress: Float
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "Determinate ProgressBar",
|
||||
color = brandTextColor,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
|
||||
text = "Linear Progressbar",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
Modifier.height(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
LinearProgressIndicator(linearProgress, modifier = Modifier.width(240.dp))
|
||||
Text(text = "" + "%.0f".format(linearProgress * 100) + "%", color = textColor)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
|
||||
text = "Circular ProgressBar",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
Modifier.height(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
CircularProgressIndicator(circularProgress, size = CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
|
||||
Text(text = "" + "%.0f".format(circularProgress * 100) + "%", color = textColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IndeterminateProgressBarExamples(brandTextColor: Color, textColor: Color) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "InDeterminate ProgressBar",
|
||||
color = brandTextColor,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
|
||||
text = "Linear Progressbar",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
Modifier.height(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
LinearProgressIndicator(modifier = Modifier.width(240.dp))
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
|
||||
text = "Circular ProgressBar",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
Modifier.height(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp)
|
||||
) {
|
||||
CircularProgressIndicator(size = CircularProgressIndicatorSize.XLarge, style = FluentStyle.Brand)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun shimmerExamples(brandTextColor: Color, textColor: Color) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "Shimmer",
|
||||
color = brandTextColor,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "Box shimmer",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 16.dp)
|
||||
.height(80.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(120.dp, 80.dp))
|
||||
Column(
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.padding(top = 10.dp, bottom = 10.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
|
||||
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
|
||||
Shimmer(modifier = Modifier.size(200.dp, 12.dp))
|
||||
}
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
text = "Circle shimmer",
|
||||
color = textColor
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 16.dp)
|
||||
.height(60.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(60.dp, 60.dp), shape = ShimmerShape.Circle)
|
||||
Column(
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.padding(top = 10.dp, bottom = 10.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
|
||||
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 16.dp)
|
||||
.height(60.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(60.dp, 60.dp), shape = ShimmerShape.Circle)
|
||||
Column(
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.padding(top = 10.dp, bottom = 10.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
|
||||
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 16.dp)
|
||||
.height(60.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(60.dp), shape = ShimmerShape.Circle)
|
||||
Column(
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.padding(top = 10.dp, bottom = 10.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Shimmer(modifier = Modifier.size(140.dp, 12.dp))
|
||||
Shimmer(modifier = Modifier.size(180.dp, 12.dp))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,15 +21,18 @@ class ControlTokens {
|
|||
BottomSheet,
|
||||
Button,
|
||||
CheckBox,
|
||||
CircularProgressIndicator,
|
||||
ContextualCommandBar,
|
||||
Drawer,
|
||||
FloatingActionButton,
|
||||
LinearProgressIndicator,
|
||||
ListItem,
|
||||
PillButton,
|
||||
PillBar,
|
||||
RadioButton,
|
||||
PillSwitch,
|
||||
PillTabs,
|
||||
Shimmer,
|
||||
ToggleSwitch
|
||||
}
|
||||
|
||||
|
@ -42,15 +45,18 @@ class ControlTokens {
|
|||
ControlType.BottomSheet -> BottomSheetTokens()
|
||||
ControlType.Button -> ButtonTokens()
|
||||
ControlType.CheckBox -> CheckBoxTokens()
|
||||
ControlType.CircularProgressIndicator -> CircularProgressIndicatorTokens()
|
||||
ControlType.ContextualCommandBar -> ContextualCommandBarTokens()
|
||||
ControlType.Drawer -> DrawerTokens()
|
||||
ControlType.FloatingActionButton -> FABTokens()
|
||||
ControlType.LinearProgressIndicator -> LinearProgressIndicatorTokens()
|
||||
ControlType.ListItem -> ListItemTokens()
|
||||
ControlType.PillButton -> PillButtonTokens()
|
||||
ControlType.PillBar -> PillBarTokens()
|
||||
ControlType.RadioButton -> RadioButtonTokens()
|
||||
ControlType.PillSwitch -> PillSwitchTokens()
|
||||
ControlType.PillTabs -> PillTabsTokens()
|
||||
ControlType.Shimmer -> ShimmerTokens()
|
||||
ControlType.ToggleSwitch -> ToggleSwitchTokens()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package com.microsoft.fluentui.theme.token.controlTokens
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
enum class CircularProgressIndicatorSize {
|
||||
XXSmall,
|
||||
XSmall,
|
||||
Medium,
|
||||
Large,
|
||||
XLarge
|
||||
}
|
||||
|
||||
data class CircularProgressIndicatorInfo(
|
||||
val circularProgressIndicatorSize: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XSmall,
|
||||
val style: FluentStyle = FluentStyle.Neutral
|
||||
) : ControlInfo
|
||||
|
||||
@Parcelize
|
||||
open class CircularProgressIndicatorTokens : ControlToken, Parcelable {
|
||||
|
||||
@Composable
|
||||
open fun size(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Dp {
|
||||
return when (circularProgressIndicatorInfo.circularProgressIndicatorSize) {
|
||||
CircularProgressIndicatorSize.XXSmall -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XXSmall).size
|
||||
CircularProgressIndicatorSize.XSmall -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XSmall).size
|
||||
CircularProgressIndicatorSize.Medium -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.Medium).size
|
||||
CircularProgressIndicatorSize.Large -> 32.dp
|
||||
CircularProgressIndicatorSize.XLarge -> GlobalTokens.iconSize(GlobalTokens.IconSizeTokens.XLarge).size
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
open fun strokeWidth(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Dp {
|
||||
return when (circularProgressIndicatorInfo.circularProgressIndicatorSize) {
|
||||
CircularProgressIndicatorSize.XXSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thinner)
|
||||
CircularProgressIndicatorSize.XSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thinner)
|
||||
CircularProgressIndicatorSize.Medium -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thick)
|
||||
CircularProgressIndicatorSize.Large -> 3.dp
|
||||
CircularProgressIndicatorSize.XLarge -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thicker)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
open fun color(circularProgressIndicatorInfo: CircularProgressIndicatorInfo): Color {
|
||||
return if (circularProgressIndicatorInfo.style == FluentStyle.Neutral) {
|
||||
FluentColor(
|
||||
light = GlobalTokens.neutralColor(GlobalTokens.NeutralColorTokens.Grey56),
|
||||
dark = GlobalTokens.neutralColor(GlobalTokens.NeutralColorTokens.Grey72)
|
||||
).value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
} else {
|
||||
FluentTheme.aliasTokens.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.microsoft.fluentui.theme.token.controlTokens
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.AliasTokens
|
||||
import com.microsoft.fluentui.theme.token.ControlInfo
|
||||
import com.microsoft.fluentui.theme.token.ControlToken
|
||||
import com.microsoft.fluentui.theme.token.GlobalTokens
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
enum class LinearProgressIndicatorHeight{
|
||||
XXXSmall
|
||||
}
|
||||
data class LinearProgressIndicatorInfo(
|
||||
val linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
|
||||
): ControlInfo
|
||||
|
||||
@Parcelize
|
||||
open class LinearProgressIndicatorTokens: ControlToken, Parcelable{
|
||||
|
||||
@Composable
|
||||
open fun strokeWidth(linearProgressIndicatorInfo: LinearProgressIndicatorInfo):Dp{
|
||||
return when(linearProgressIndicatorInfo.linearProgressIndicatorHeight){
|
||||
LinearProgressIndicatorHeight.XXXSmall -> GlobalTokens.strokeWidth(GlobalTokens.StrokeWidthTokens.Thick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
open fun backgroundColor(linearProgressIndicatorInfo: LinearProgressIndicatorInfo): Color {
|
||||
return FluentTheme.aliasTokens.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.Stroke1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
}
|
||||
@Composable
|
||||
open fun color(linearProgressIndicatorInfo: LinearProgressIndicatorInfo): Color {
|
||||
return FluentTheme.aliasTokens.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.microsoft.fluentui.theme.token.controlTokens
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.AliasTokens
|
||||
import com.microsoft.fluentui.theme.token.ControlToken
|
||||
import com.microsoft.fluentui.theme.token.GlobalTokens
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
enum class ShimmerShape {
|
||||
Box,
|
||||
Circle
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
open class ShimmerTokens : ControlToken, Parcelable {
|
||||
@Composable
|
||||
open fun cornerRadius(): Dp {
|
||||
return GlobalTokens.borderRadius(GlobalTokens.BorderRadiusTokens.Medium)
|
||||
}
|
||||
|
||||
@Composable
|
||||
open fun knockoutEffectColor(): Color {
|
||||
return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Stencil2].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
open fun color(): Color {
|
||||
return FluentTheme.aliasTokens.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Stencil1].value(
|
||||
themeMode = FluentTheme.themeMode
|
||||
)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,16 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
useIR = true
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion composeVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -40,6 +50,11 @@ dependencies {
|
|||
implementation "com.google.android.material:material:$materialVersion"
|
||||
androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||
|
||||
implementation("androidx.compose.foundation:foundation:$composeVersion")
|
||||
implementation("androidx.compose.material:material:$composeVersion")
|
||||
implementation("androidx.compose.runtime:runtime:$composeVersion")
|
||||
implementation("androidx.compose.ui:ui:$composeVersion")
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
package com.microsoft.fluentui.tokenized.progress
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.ControlTokens
|
||||
import com.microsoft.fluentui.theme.token.FluentStyle
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorInfo
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorSize
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.CircularProgressIndicatorTokens
|
||||
import com.microsoft.fluentui.util.dpToPx
|
||||
|
||||
val LocalCircularProgressIndicatorTokens = compositionLocalOf { CircularProgressIndicatorTokens() }
|
||||
val LocalCircularProgressIndicatorInfo = compositionLocalOf { CircularProgressIndicatorInfo() }
|
||||
|
||||
@Composable
|
||||
fun getCircularProgressIndicatorTokens(): CircularProgressIndicatorTokens {
|
||||
return LocalCircularProgressIndicatorTokens.current
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getCircularProgressIndicatorInfo(): CircularProgressIndicatorInfo {
|
||||
return LocalCircularProgressIndicatorInfo.current
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Determinate Circular Progress Indicator
|
||||
*
|
||||
* @param progress Progress of the progress indicator. 0.0 represents no progress and 1.0 represents full progress.
|
||||
* @param size Optional size of the circular progress indicator
|
||||
* @param modifier Modifier for circular progress indicator
|
||||
* @param style Style of progress indicator. Default: [FluentStyle.Neutral]
|
||||
* @param circularProgressIndicatorTokens Token values for circular progress indicator
|
||||
*
|
||||
*/
|
||||
@Composable
|
||||
fun CircularProgressIndicator(
|
||||
progress: Float,
|
||||
size: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XXSmall,
|
||||
modifier: Modifier = Modifier,
|
||||
style: FluentStyle = FluentStyle.Neutral,
|
||||
circularProgressIndicatorTokens: CircularProgressIndicatorTokens? = null
|
||||
) {
|
||||
val tokens = circularProgressIndicatorTokens
|
||||
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.CircularProgressIndicator] as CircularProgressIndicatorTokens
|
||||
CompositionLocalProvider(
|
||||
LocalCircularProgressIndicatorTokens provides tokens,
|
||||
LocalCircularProgressIndicatorInfo provides CircularProgressIndicatorInfo(
|
||||
circularProgressIndicatorSize = size,
|
||||
style = style
|
||||
)
|
||||
) {
|
||||
val currentProgress = animateFloatAsState(
|
||||
targetValue = progress.coerceIn(0f..1f),
|
||||
animationSpec = tween(
|
||||
delayMillis = 0,
|
||||
durationMillis = 750,
|
||||
easing = LinearOutSlowInEasing
|
||||
)
|
||||
)
|
||||
val circularProgressIndicatorColor =
|
||||
getCircularProgressIndicatorTokens().color(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val circularProgressIndicatorSize =
|
||||
getCircularProgressIndicatorTokens().size(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val circularProgressIndicatorStrokeWidth =
|
||||
getCircularProgressIndicatorTokens().strokeWidth(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val indicatorSizeInPx = dpToPx(circularProgressIndicatorSize)
|
||||
Canvas(modifier = modifier.requiredSize(circularProgressIndicatorSize)) {
|
||||
drawArc(
|
||||
circularProgressIndicatorColor,
|
||||
-90f,
|
||||
currentProgress.value * 360,
|
||||
false,
|
||||
size = Size(
|
||||
indicatorSizeInPx,
|
||||
indicatorSizeInPx
|
||||
),
|
||||
style = Stroke(dpToPx(circularProgressIndicatorStrokeWidth), cap = StrokeCap.Round)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Indeterminate Circular Progress indicator
|
||||
*
|
||||
* @param size Optional size of the circular progress indicator
|
||||
* @param modifier Modifier for circular progress indicator
|
||||
* @param style Style of progress indicator. Default: [FluentStyle.Neutral]
|
||||
* @param circularProgressIndicatorTokens Token values for circular progress indicator
|
||||
*
|
||||
*/
|
||||
@Composable
|
||||
fun CircularProgressIndicator(
|
||||
size: CircularProgressIndicatorSize = CircularProgressIndicatorSize.XXSmall,
|
||||
modifier: Modifier = Modifier,
|
||||
style: FluentStyle = FluentStyle.Neutral,
|
||||
circularProgressIndicatorTokens: CircularProgressIndicatorTokens? = null
|
||||
) {
|
||||
val tokens = circularProgressIndicatorTokens
|
||||
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.CircularProgressIndicator] as CircularProgressIndicatorTokens
|
||||
CompositionLocalProvider(
|
||||
LocalCircularProgressIndicatorTokens provides tokens,
|
||||
LocalCircularProgressIndicatorInfo provides CircularProgressIndicatorInfo(
|
||||
circularProgressIndicatorSize = size,
|
||||
style = style
|
||||
)
|
||||
) {
|
||||
val circularProgressIndicatorColor =
|
||||
getCircularProgressIndicatorTokens().color(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val circularProgressIndicatorSize =
|
||||
getCircularProgressIndicatorTokens().size(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val circularProgressIndicatorStrokeWidth =
|
||||
getCircularProgressIndicatorTokens().strokeWidth(
|
||||
getCircularProgressIndicatorInfo()
|
||||
)
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val startAngle by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
360f,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 1000,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val indicatorSizeInPx = dpToPx(circularProgressIndicatorSize)
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.requiredSize(circularProgressIndicatorSize)
|
||||
.rotate(startAngle)
|
||||
) {
|
||||
drawArc(
|
||||
Brush.sweepGradient(
|
||||
0f to Color.Transparent,
|
||||
0.6f to circularProgressIndicatorColor
|
||||
),
|
||||
0f,
|
||||
270f,
|
||||
false,
|
||||
size = Size(
|
||||
indicatorSizeInPx, indicatorSizeInPx
|
||||
),
|
||||
style = Stroke(dpToPx(circularProgressIndicatorStrokeWidth))
|
||||
)
|
||||
drawCircle(
|
||||
color = circularProgressIndicatorColor,
|
||||
radius = dpToPx(circularProgressIndicatorStrokeWidth) / 2,
|
||||
center = Offset(indicatorSizeInPx / 2, 0f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
package com.microsoft.fluentui.tokenized.progress
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.ControlTokens
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorHeight
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorInfo
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.LinearProgressIndicatorTokens
|
||||
import com.microsoft.fluentui.util.dpToPx
|
||||
|
||||
val LocalLinearProgressIndicatorTokens = compositionLocalOf { LinearProgressIndicatorTokens() }
|
||||
val LocalLinearProgressIndicatorInfo = compositionLocalOf { LinearProgressIndicatorInfo() }
|
||||
|
||||
@Composable
|
||||
fun getLinearProgressIndicatorTokens(): LinearProgressIndicatorTokens {
|
||||
return LocalLinearProgressIndicatorTokens.current
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getLinearProgressIndicatorInfo(): LinearProgressIndicatorInfo {
|
||||
return LocalLinearProgressIndicatorInfo.current
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Determinate Linear Progress Indicator
|
||||
*
|
||||
* @param progress Progress of the progress indicator. 0.0 represents no progress and 1.0 represents full progress.
|
||||
* @param linearProgressIndicatorHeight Optional width of the progress indicator
|
||||
* @param modifier Modifier for linear progress indicator
|
||||
* @param linearProgressIndicatorTokens Token values for linear progress indicator
|
||||
*
|
||||
*/
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
progress: Float,
|
||||
linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
|
||||
modifier: Modifier = Modifier,
|
||||
linearProgressIndicatorTokens: LinearProgressIndicatorTokens? = null
|
||||
) {
|
||||
val tokens = linearProgressIndicatorTokens
|
||||
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.LinearProgressIndicator] as LinearProgressIndicatorTokens
|
||||
CompositionLocalProvider(
|
||||
LocalLinearProgressIndicatorTokens provides tokens,
|
||||
LocalLinearProgressIndicatorInfo provides LinearProgressIndicatorInfo(
|
||||
linearProgressIndicatorHeight = linearProgressIndicatorHeight
|
||||
)
|
||||
) {
|
||||
val currentProgress = animateFloatAsState(
|
||||
targetValue = progress.coerceIn(0f..1f),
|
||||
animationSpec = tween(
|
||||
delayMillis = 0,
|
||||
durationMillis = 1000,
|
||||
easing = LinearOutSlowInEasing
|
||||
)
|
||||
)
|
||||
val linearProgressIndicatorHeight =
|
||||
getLinearProgressIndicatorTokens().strokeWidth(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
val linearProgressIndicatorBackgroundColor =
|
||||
getLinearProgressIndicatorTokens().backgroundColor(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
val linearProgressIndicatorColor =
|
||||
getLinearProgressIndicatorTokens().color(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(linearProgressIndicatorHeight)
|
||||
) {
|
||||
val strokeWidth = dpToPx(linearProgressIndicatorHeight)
|
||||
val yOffset = strokeWidth / 2
|
||||
drawLine(
|
||||
linearProgressIndicatorBackgroundColor,
|
||||
Offset(0f, yOffset),
|
||||
Offset(size.width, yOffset),
|
||||
strokeWidth
|
||||
)
|
||||
drawLine(
|
||||
linearProgressIndicatorColor,
|
||||
Offset(0f, yOffset),
|
||||
Offset(currentProgress.value * (size.width), yOffset),
|
||||
strokeWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Indeterminate Linear Progress Indicator
|
||||
*
|
||||
* @param linearProgressIndicatorHeight Optional width of the progress indicator
|
||||
* @param modifier Modifier for linear progress indicator
|
||||
* @param linearProgressIndicatorTokens Token values for linear progress indicator
|
||||
*
|
||||
*/
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
linearProgressIndicatorHeight: LinearProgressIndicatorHeight = LinearProgressIndicatorHeight.XXXSmall,
|
||||
modifier: Modifier = Modifier,
|
||||
linearProgressIndicatorTokens: LinearProgressIndicatorTokens? = null
|
||||
) {
|
||||
val tokens = linearProgressIndicatorTokens
|
||||
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.LinearProgressIndicator] as LinearProgressIndicatorTokens
|
||||
CompositionLocalProvider(
|
||||
LocalLinearProgressIndicatorTokens provides tokens,
|
||||
LocalLinearProgressIndicatorInfo provides LinearProgressIndicatorInfo(
|
||||
linearProgressIndicatorHeight = linearProgressIndicatorHeight
|
||||
)
|
||||
) {
|
||||
val linearProgressIndicatorHeight =
|
||||
getLinearProgressIndicatorTokens().strokeWidth(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
val linearProgressIndicatorBackgroundColor =
|
||||
getLinearProgressIndicatorTokens().backgroundColor(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
val linearProgressIndicatorColor =
|
||||
getLinearProgressIndicatorTokens().color(
|
||||
getLinearProgressIndicatorInfo()
|
||||
)
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val animationDuration = (1750 * 0.5f).toInt()
|
||||
val headAnimationDelay = 0
|
||||
val tailAnimationDelay = (animationDuration * 0.5f).toInt()
|
||||
val indicatorHead by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = animationDuration + 500
|
||||
0f at headAnimationDelay with FastOutSlowInEasing
|
||||
1f at animationDuration + headAnimationDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
val indicatorTail by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = animationDuration + 500
|
||||
0f at tailAnimationDelay with FastOutSlowInEasing
|
||||
1f at animationDuration + tailAnimationDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(linearProgressIndicatorHeight)
|
||||
) {
|
||||
val strokeWidth = dpToPx(linearProgressIndicatorHeight)
|
||||
val yOffset = strokeWidth / 2
|
||||
drawLine(
|
||||
linearProgressIndicatorBackgroundColor,
|
||||
Offset(0f, yOffset),
|
||||
Offset(size.width, yOffset),
|
||||
strokeWidth
|
||||
)
|
||||
drawLine(
|
||||
Brush.linearGradient(
|
||||
0f to linearProgressIndicatorBackgroundColor,
|
||||
0.5f to linearProgressIndicatorColor,
|
||||
1.0f to linearProgressIndicatorBackgroundColor,
|
||||
start = Offset(indicatorHead * size.width, yOffset),
|
||||
end = Offset(indicatorTail * size.width, yOffset)
|
||||
),
|
||||
Offset(indicatorHead * size.width, yOffset),
|
||||
Offset(indicatorTail * size.width, yOffset),
|
||||
strokeWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package com.microsoft.fluentui.tokenized.progress
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.microsoft.fluentui.theme.FluentTheme
|
||||
import com.microsoft.fluentui.theme.token.ControlTokens
|
||||
import com.microsoft.fluentui.theme.token.controlTokens.*
|
||||
import com.microsoft.fluentui.util.dpToPx
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
val LocalShimmerTokens = compositionLocalOf { ShimmerTokens() }
|
||||
|
||||
@Composable
|
||||
fun getShimmerTokens(): ShimmerTokens {
|
||||
return LocalShimmerTokens.current
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Shimmer effect
|
||||
*
|
||||
* @param shape Shape of the shimmer. See [ShimmerShape] for shapes
|
||||
* @param modifier Modifier for shimmer
|
||||
* @param shimmerTokens Token values for shimmer
|
||||
*
|
||||
*/
|
||||
@Composable
|
||||
fun Shimmer(
|
||||
shape: ShimmerShape = ShimmerShape.Box,
|
||||
modifier: Modifier = Modifier,
|
||||
shimmerTokens: ShimmerTokens? = null
|
||||
) {
|
||||
val tokens = shimmerTokens
|
||||
?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.Shimmer] as ShimmerTokens
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenHeight = dpToPx(configuration.screenHeightDp.dp)
|
||||
val screenWidth = dpToPx(configuration.screenWidthDp.dp)
|
||||
val diagonal =
|
||||
Math.sqrt((screenHeight * screenHeight + screenWidth * screenWidth).toDouble()).toFloat()
|
||||
CompositionLocalProvider(
|
||||
LocalShimmerTokens provides tokens
|
||||
) {
|
||||
val shimmerBackgroundColor =
|
||||
getShimmerTokens().color()
|
||||
val shimmerKnockoutEffectColor = getShimmerTokens().knockoutEffectColor()
|
||||
val cornerRadius =
|
||||
dpToPx(getShimmerTokens().cornerRadius())
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val shimmerEffect by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
diagonal,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = 1000,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val gradientColor = Brush.linearGradient(
|
||||
0f to shimmerBackgroundColor,
|
||||
0.5f to shimmerKnockoutEffectColor,
|
||||
1.0f to shimmerBackgroundColor,
|
||||
start = Offset.Zero,
|
||||
end = Offset(shimmerEffect.absoluteValue, shimmerEffect.absoluteValue)
|
||||
)
|
||||
if (shape == ShimmerShape.Box) {
|
||||
Spacer(
|
||||
modifier = modifier
|
||||
.width(240.dp)
|
||||
.height(12.dp)
|
||||
.clip(RoundedCornerShape(cornerRadius))
|
||||
.background(gradientColor)
|
||||
)
|
||||
} else {
|
||||
Spacer(
|
||||
modifier = modifier
|
||||
.size(60.dp)
|
||||
.clip(CircleShape)
|
||||
.background(gradientColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче