From df27d69bbb8f231d32ee7613d76fdb4892b1b0c3 Mon Sep 17 00:00:00 2001 From: SerraMorec Date: Wed, 6 May 2026 14:27:11 +0300 Subject: [PATCH] feat(sdds-acore/uikit-compose): Motion API was added in Badge and IconBadge --- .../kotlin/com/sdds/compose/uikit/Badge.kt | 127 ++++-- .../sdds/compose/uikit/BadgeStyleBuilder.kt | 368 +++++++++++++----- .../com/sdds/compose/uikit/BadgeStyles.kt | 121 ++++++ .../kotlin/com/sdds/compose/uikit/Chip.kt | 20 +- .../compose/uikit/IconBadgeStyleBuilder.kt | 256 ++++++++---- .../kotlin/com/sdds/compose/uikit/Toast.kt | 9 +- .../uikit/internal/icontext/BaseIconText.kt | 64 +-- .../motion/components/badge/BadgeMotion.kt | 22 ++ .../components/badge/BadgeMotionStyle.kt | 155 ++++++++ .../components/badge/IconBadgeMotion.kt | 22 ++ .../components/badge/IconBadgeMotionStyle.kt | 86 ++++ 11 files changed, 1028 insertions(+), 222 deletions(-) create mode 100644 sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotion.kt create mode 100644 sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotionStyle.kt create mode 100644 sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotion.kt create mode 100644 sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotionStyle.kt diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Badge.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Badge.kt index d9c4f5b0fe..b6db48a0a4 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Badge.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Badge.kt @@ -1,12 +1,21 @@ package com.sdds.compose.uikit +import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.SolidColor +import com.sdds.compose.uikit.interactions.getValue import com.sdds.compose.uikit.internal.common.surface import com.sdds.compose.uikit.internal.icontext.BaseIconText +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.components.badge.BadgeMotionStyle +import com.sdds.compose.uikit.motion.components.badge.IconBadgeMotionStyle +import com.sdds.compose.uikit.motion.components.badge.rememberBadgeMotion +import com.sdds.compose.uikit.motion.components.badge.rememberIconBadgeMotion +import com.sdds.compose.uikit.motion.getBrushAsState +import com.sdds.compose.uikit.motion.getTextStyleAsState +import com.sdds.compose.uikit.motion.rememberMotionContext /** * Компонент Badge с текстом и иконкой @@ -17,6 +26,7 @@ import com.sdds.compose.uikit.internal.icontext.BaseIconText * @param startContent контент в начале * @param endContent контент в конце * @param interactionSource источник взаимодействий + * @param motion объект анимаций */ @Composable fun Badge( @@ -26,21 +36,30 @@ fun Badge( startContent: (@Composable () -> Unit)? = null, endContent: (@Composable () -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + motion: Motion = rememberBadgeMotion( + motionContext = rememberMotionContext(interactionSource), + ), ) { - val labelStyle = style.labelStyle - val dimens = style.dimensions.toDimensionsSet() - val colors = style.colors.toColorsSet() - val background = style.colors.backgroundColor.colorForInteractionAsState(interactionSource) + val labelStyle = style.labelStyles.getTextStyleAsState( + motion.context, + motion.style.labelStyle, + ) + val dimens = style.dimensions.toDimensionsSet(interactionSource) + val colors = style.colors.toBadgeColorsSet(motion) + val background = style.colors.backgroundBrush.getBrushAsState( + motion.context, + motion.style.backgroundColor, + ) BaseIconText( modifier = modifier.surface( shape = style.shape, - backgroundColor = { SolidColor(background.value) }, + backgroundColor = { background.value }, interactionSource = interactionSource, ), dimensionsSet = dimens, colorsSet = colors, labelContent = label, - labelStyle = labelStyle, + labelStyle = labelStyle.value, startContent = startContent, endContent = endContent, interactionSource = interactionSource, @@ -56,6 +75,7 @@ fun Badge( * @param startContent контент в начале * @param endContent контент в конце * @param interactionSource источник взаимодействий + * @param motion объект анимаций */ @Composable fun Badge( @@ -65,14 +85,20 @@ fun Badge( startContent: (@Composable () -> Unit)? = null, endContent: (@Composable () -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + motion: Motion = rememberBadgeMotion( + motionContext = rememberMotionContext(interactionSource), + ), ) { - val dimens = style.dimensions.toDimensionsSet() - val colors = style.colors.toColorsSet() - val background = style.colors.backgroundColor.colorForInteractionAsState(interactionSource) + val dimens = style.dimensions.toDimensionsSet(interactionSource) + val colors = style.colors.toBadgeColorsSet(motion) + val background = style.colors.backgroundBrush.getBrushAsState( + motion.context, + motion.style.backgroundColor, + ) BaseIconText( modifier = modifier.surface( shape = style.shape, - backgroundColor = { SolidColor(background.value) }, + backgroundColor = { background.value }, interactionSource = interactionSource, ), dimensionsSet = dimens, @@ -92,6 +118,7 @@ fun Badge( * @param style стиль компонента * @param content контент (иконка) * @param interactionSource источник взаимодействий + * @param motion объект анимаций */ @Composable fun IconBadge( @@ -99,14 +126,20 @@ fun IconBadge( style: BadgeStyle = LocalIconBadgeStyle.current, content: (@Composable () -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + motion: Motion = rememberIconBadgeMotion( + motionContext = rememberMotionContext(interactionSource), + ), ) { - val dimens = style.dimensions.toDimensionsSet() - val colors = style.colors.toColorsSet() - val background = style.colors.backgroundColor.colorForInteractionAsState(interactionSource) + val dimens = style.dimensions.toDimensionsSet(interactionSource) + val colors = style.colors.toIconBadgeColorsSet(motion) + val background = style.colors.backgroundBrush.getBrushAsState( + motion.context, + motion.style.backgroundColor, + ) BaseIconText( modifier = modifier.surface( shape = style.shape, - backgroundColor = { SolidColor(background.value) }, + backgroundColor = { background.value }, interactionSource = interactionSource, ), dimensionsSet = dimens, @@ -116,23 +149,59 @@ fun IconBadge( ) } -internal fun BadgeDimensions.toDimensionsSet(): BaseIconText.Dimensions { +@Composable +internal fun BadgeDimensions.toDimensionsSet( + interactionSource: InteractionSource, +): BaseIconText.Dimensions { + // В будущем нужно будет получить из BadgeMotionStyle + // Dp values и прокинуть их в маппер return BaseIconText.Dimensions( - height = this.height, - endContentSize = this.endContentSize, - startContentSize = this.startContentSize, - endContentMargin = this.endContentMargin, - startContentMargin = this.startContentMargin, - endPadding = this.endPadding, - startPadding = this.startPadding, + height = this.heightValues.getValue(interactionSource), + endContentSize = this.endContentSizeValues.getValue(interactionSource), + startContentSize = this.startContentSizeValues.getValue(interactionSource), + endContentMargin = this.endContentMarginValues.getValue(interactionSource), + startContentMargin = this.startContentMarginValues.getValue(interactionSource), + endPadding = this.endPaddingValues, + startPadding = this.startPaddingValues, ) } -internal fun BadgeColors.toColorsSet(): BaseIconText.Colors { - return BaseIconText.Colors( - contentColor = this.contentColor, - labelColor = this.labelColor, - startContentColor = this.startContentColor, - endContentColor = this.endContentColor, +@Composable +internal fun BadgeColors.toBadgeColorsSet( + motion: Motion, +): BaseIconText.Brushes { + return BaseIconText.Brushes( + contentBrush = this.contentBrush.getBrushAsState( + motion.context, + motion.style.contentColor, + ).value, + labelBrush = this.labelBrush.getBrushAsState( + motion.context, + motion.style.labelColor, + ).value, + startContentBrush = this.startContentBrush.getBrushAsState( + motion.context, + motion.style.startContentColor, + ).value, + endContentBrush = this.endContentBrush.getBrushAsState( + motion.context, + motion.style.endContentColor, + ).value, + ) +} + +@Composable +internal fun BadgeColors.toIconBadgeColorsSet( + motion: Motion, +): BaseIconText.Brushes { + return BaseIconText.Brushes( + contentBrush = this.startContentBrush.getBrushAsState( + motion.context, + motion.style.startContentColor, + ).value, + startContentBrush = this.startContentBrush.getBrushAsState( + motion.context, + motion.style.startContentColor, + ).value, ) } diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyleBuilder.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyleBuilder.kt index ed556474a8..40cd0d0f0f 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyleBuilder.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyleBuilder.kt @@ -5,12 +5,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.sdds.compose.uikit.graphics.brush.asStatefulBrush import com.sdds.compose.uikit.interactions.InteractiveColor +import com.sdds.compose.uikit.interactions.StatefulValue import com.sdds.compose.uikit.interactions.asInteractive +import com.sdds.compose.uikit.interactions.asStatefulBrush +import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.StyleBuilder /** @@ -47,7 +52,13 @@ interface BadgeStyleBuilder : StyleBuilder { * Устанавливает стиль основного текста компонента [labelStyle] * @see BadgeStyle.labelStyle */ - fun labelStyle(labelStyle: TextStyle): BadgeStyleBuilder + fun labelStyle(labelStyle: TextStyle): BadgeStyleBuilder = + labelStyle(labelStyle.asStatefulValue()) + + /** + * Устанавливает стиль основного текста компонента [labelStyle] + */ + fun labelStyle(labelStyle: StatefulValue): BadgeStyleBuilder /** * Устанавливает размеры и отступы компонента [dimensions] @@ -64,37 +75,79 @@ interface BadgeDimensionsBuilder { /** * Устанавливает высоту Badge */ - fun height(height: Dp): BadgeDimensionsBuilder + fun height(height: Dp): BadgeDimensionsBuilder = + height(height.asStatefulValue()) + + /** + * Устанавливает высоту Badge + */ + fun height(values: StatefulValue): BadgeDimensionsBuilder + + /** + * Устанавливает размер иконки в конце + */ + fun endContentSize(endContentSize: Dp): BadgeDimensionsBuilder = + endContentSize(endContentSize.asStatefulValue()) /** * Устанавливает размер иконки в конце */ - fun endContentSize(endContentSize: Dp): BadgeDimensionsBuilder + fun endContentSize(values: StatefulValue): BadgeDimensionsBuilder /** * Устанавливает размер иконки в начале */ - fun startContentSize(startContentSize: Dp): BadgeDimensionsBuilder + fun startContentSize(startContentSize: Dp): BadgeDimensionsBuilder = + startContentSize(startContentSize.asStatefulValue()) + + /** + * Устанавливает размер иконки в начале + */ + fun startContentSize(values: StatefulValue): BadgeDimensionsBuilder + + /** + * Устанавливает отступы контента в начале + */ + fun startContentMargin(startContentMargin: Dp): BadgeDimensionsBuilder = + startContentMargin(startContentMargin.asStatefulValue()) /** * Устанавливает отступы контента в начале */ - fun startContentMargin(startContentMargin: Dp): BadgeDimensionsBuilder + fun startContentMargin(values: StatefulValue): BadgeDimensionsBuilder /** * Устанавливает отступы контента в конце */ - fun endContentMargin(endContentMargin: Dp): BadgeDimensionsBuilder + fun endContentMargin(endContentMargin: Dp): BadgeDimensionsBuilder = + endContentMargin(endContentMargin.asStatefulValue()) + + /** + * Устанавливает отступы контента в конце + */ + fun endContentMargin(values: StatefulValue): BadgeDimensionsBuilder + + /** + * Устанавливает отступ от начала компонента до контента + */ + fun startPadding(startPadding: Dp): BadgeDimensionsBuilder = + startPadding(startPadding.asStatefulValue()) /** * Устанавливает отступ от начала компонента до контента */ - fun startPadding(startPadding: Dp): BadgeDimensionsBuilder + fun startPadding(values: StatefulValue): BadgeDimensionsBuilder + + /** + * Устанавливает отступ от контента до конца компонента + */ + fun endPadding(endPadding: Dp): BadgeDimensionsBuilder = + endPadding(endPadding.asStatefulValue()) /** * Устанавливает отступ от контента до конца компонента */ - fun endPadding(endPadding: Dp): BadgeDimensionsBuilder + fun endPadding(values: StatefulValue): BadgeDimensionsBuilder /** * Возвращает готовый экземпляр [BadgeDimensions] @@ -119,75 +172,131 @@ interface BadgeColorsBuilder { * @see BadgeColors.contentColor * @see InteractiveColor */ - fun contentColor(contentColor: InteractiveColor): BadgeColorsBuilder + fun contentColor(contentColor: InteractiveColor): BadgeColorsBuilder = + contentBrush(contentColor.asStatefulBrush()) /** * Устанавливает цвет контента компонента [contentColor] * @see BadgeColorsBuilder.contentColor * @see BadgeColors.contentColor - * @see InteractiveColor */ fun contentColor(contentColor: Color): BadgeColorsBuilder = - contentColor(contentColor.asInteractive()) + contentBrush(contentColor.asStatefulBrush()) + + /** + * Устанавливает цвет контента + */ + fun contentColor(contentColor: StatefulValue): BadgeColorsBuilder = + contentBrush(contentColor.asStatefulBrush()) + + /** + * Устанавливает кисть контента + */ + fun contentBrush(contentBrush: StatefulValue): BadgeColorsBuilder /** * Устанавливает цвет фона компонента [backgroundColor] * @see BadgeColors.backgroundColor * @see InteractiveColor */ - fun backgroundColor(backgroundColor: InteractiveColor): BadgeColorsBuilder + fun backgroundColor(backgroundColor: InteractiveColor): BadgeColorsBuilder = + backgroundBrush(backgroundColor.asStatefulBrush()) /** * Устанавливает цвет фона компонента [backgroundColor] * @see BadgeColors.backgroundColor */ fun backgroundColor(backgroundColor: Color): BadgeColorsBuilder = - backgroundColor(backgroundColor.asInteractive()) + backgroundBrush(backgroundColor.asStatefulBrush()) + + /** + * Устанавливает цвет фона компонента [backgroundColor] + */ + fun backgroundColor(backgroundColor: StatefulValue): BadgeColorsBuilder = + backgroundBrush(backgroundColor.asStatefulBrush()) + + /** + * Устанавливает кисть фона компонента [backgroundBrush] + */ + fun backgroundBrush(backgroundBrush: StatefulValue): BadgeColorsBuilder /** * Устанавливает цвет основного текста компонента [labelColor] * @see BadgeColors.labelColor * @see InteractiveColor */ - fun labelColor(labelColor: InteractiveColor): BadgeColorsBuilder + fun labelColor(labelColor: InteractiveColor): BadgeColorsBuilder = + labelBrush(labelColor.asStatefulBrush()) /** * Устанавливает цвет основного текста компонента [labelColor] * @see BadgeColors.labelColor - * @see InteractiveColor */ fun labelColor(labelColor: Color): BadgeColorsBuilder = - labelColor(labelColor.asInteractive()) + labelBrush(labelColor.asStatefulBrush()) + + /** + * Устанавливает цвет основного текста [labelColor] + */ + fun labelColor(labelColor: StatefulValue): BadgeColorsBuilder = + labelBrush(labelColor.asStatefulBrush()) /** - * Устанавливает цвет дополнительного текста компонента [startContentColor] + * Устанавливает кисть основного текста [labelBrush] + */ + fun labelBrush(labelBrush: StatefulValue): BadgeColorsBuilder + + /** + * Устанавливает цвет контента в начале [startContentColor] * @see BadgeColors.startContentColor * @see InteractiveColor */ - fun startContentColor(startContentColor: InteractiveColor): BadgeColorsBuilder + fun startContentColor(startContentColor: InteractiveColor): BadgeColorsBuilder = + startContentBrush(startContentColor.asStatefulBrush()) /** - * Устанавливает цвет дополнительного текста компонента [valueColor] + * Устанавливает цвет контента в начале [valueColor] * @see BadgeColors.startContentColor - * @see InteractiveColor */ fun startContentColor(valueColor: Color): BadgeColorsBuilder = - startContentColor(valueColor.asInteractive()) + startContentBrush(valueColor.asStatefulBrush()) + + /** + * Устанавливает цвет контента в начале [startContentColor] + */ + fun startContentColor(startContentColor: StatefulValue): BadgeColorsBuilder = + startContentBrush(startContentColor.asStatefulBrush()) + + /** + * Устанавливает кисть контента в начале [startContentBrush] + */ + fun startContentBrush(startContentBrush: StatefulValue): BadgeColorsBuilder /** * Устанавливает цвет иконки компонента [endContentColor] * @see BadgeColors.endContentColor * @see InteractiveColor */ - fun endContentColor(endContentColor: InteractiveColor): BadgeColorsBuilder + fun endContentColor(endContentColor: InteractiveColor): BadgeColorsBuilder = + endContentBrush(endContentColor.asStatefulBrush()) /** - * Устанавливает цвет иконки компонента [endContentColor] + * Устанавливает цвет контента в конце [endContentColor] * @see BadgeColors.endContentColor - * @see InteractiveColor */ fun endContentColor(endContentColor: Color): BadgeColorsBuilder = - endContentColor(endContentColor.asInteractive()) + endContentBrush(endContentColor.asStatefulBrush()) + + /** + * Устанавливает цвет контента в конце [endContentColor] + */ + fun endContentColor(endContentColor: StatefulValue): BadgeColorsBuilder = + endContentBrush(endContentColor.asStatefulBrush()) + + /** + * Устанавливает кисть контента в конце [endContentBrush] + */ + fun endContentBrush(endContentBrush: StatefulValue): BadgeColorsBuilder /** * Возвращает готовый экземпляр [BadgeColors] @@ -210,64 +319,107 @@ private class DefaultBadgeStyle( override val shape: CornerBasedShape, override val labelStyle: TextStyle, override val disableAlpha: Float, + override val labelStyles: StatefulValue, ) : BadgeStyle @Immutable private class DefaultBadgeDimensions( - override val height: Dp, - override val startContentSize: Dp, - override val endContentSize: Dp, - override val startContentMargin: Dp, - override val endContentMargin: Dp, - override val startPadding: Dp, - override val endPadding: Dp, + override val heightValues: StatefulValue, + override val startContentSizeValues: StatefulValue, + override val endContentSizeValues: StatefulValue, + override val startContentMarginValues: StatefulValue, + override val endContentMarginValues: StatefulValue, + override val startPaddingValues: StatefulValue, + override val endPaddingValues: StatefulValue, ) : BadgeDimensions { + @Deprecated( + "Use heightValues", + replaceWith = ReplaceWith("heightValues"), + ) + override val height: Dp = 28.dp + + @Deprecated( + "Use startContentSizeValues", + replaceWith = ReplaceWith("startContentSizeValues"), + ) + override val startContentSize: Dp = 16.dp + + @Deprecated( + "Use endContentSizeValues", + replaceWith = ReplaceWith("endContentSizeValues"), + ) + override val endContentSize: Dp = 16.dp + + @Deprecated( + "Use startContentMarginValues", + replaceWith = ReplaceWith("startContentMarginValues"), + ) + override val startContentMargin: Dp = 4.dp + + @Deprecated( + "Use endContentMarginValues", + replaceWith = ReplaceWith("endContentMarginValues"), + ) + override val endContentMargin: Dp = 4.dp + + @Deprecated( + "Use startPaddingValues", + replaceWith = ReplaceWith("startPaddingValues"), + ) + override val startPadding: Dp = 10.dp + + @Deprecated( + "Use endPaddingValues", + replaceWith = ReplaceWith("endPaddingValues"), + ) + override val endPadding: Dp = 10.dp class Builder : BadgeDimensionsBuilder { - private var height: Dp? = null - private var endContentSize: Dp? = null - private var startContentSize: Dp? = null - private var startContentMargin: Dp? = null - private var endContentMargin: Dp? = null - private var startPadding: Dp? = null - private var endPadding: Dp? = null - override fun height(height: Dp): BadgeDimensionsBuilder = apply { - this.height = height + private var heightValues: StatefulValue? = null + private var endContentSizeValues: StatefulValue? = null + private var startContentSizeValues: StatefulValue? = null + private var startContentMarginValues: StatefulValue? = null + private var endContentMarginValues: StatefulValue? = null + private var startPaddingValues: StatefulValue? = null + private var endPaddingValues: StatefulValue? = null + + override fun height(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.heightValues = values } - override fun endContentSize(endContentSize: Dp): BadgeDimensionsBuilder = apply { - this.endContentSize = endContentSize + override fun endContentSize(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.endContentSizeValues = values } - override fun startContentSize(startContentSize: Dp): BadgeDimensionsBuilder = apply { - this.startContentSize = startContentSize + override fun startContentSize(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.startContentSizeValues = values } - override fun startContentMargin(startContentMargin: Dp): BadgeDimensionsBuilder = apply { - this.startContentMargin = startContentMargin + override fun startContentMargin(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.startContentMarginValues = values } - override fun endContentMargin(endContentMargin: Dp): BadgeDimensionsBuilder = apply { - this.endContentMargin = endContentMargin + override fun endContentMargin(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.endContentMarginValues = values } - override fun startPadding(startPadding: Dp): BadgeDimensionsBuilder = apply { - this.startPadding = startPadding + override fun startPadding(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.startPaddingValues = values } - override fun endPadding(endPadding: Dp): BadgeDimensionsBuilder = apply { - this.endPadding = endPadding + override fun endPadding(values: StatefulValue): BadgeDimensionsBuilder = apply { + this.endPaddingValues = values } override fun build(): BadgeDimensions { return DefaultBadgeDimensions( - height = height ?: 28.dp, - startContentSize = startContentSize ?: 16.dp, - endContentSize = endContentSize ?: 16.dp, - startContentMargin = startContentMargin ?: 4.dp, - endContentMargin = endContentMargin ?: 4.dp, - startPadding = startPadding ?: 10.dp, - endPadding = endPadding ?: 10.dp, + heightValues = heightValues ?: 28.dp.asStatefulValue(), + startContentSizeValues = startContentSizeValues ?: 16.dp.asStatefulValue(), + endContentSizeValues = endContentSizeValues ?: 16.dp.asStatefulValue(), + startContentMarginValues = startContentMarginValues ?: 4.dp.asStatefulValue(), + endContentMarginValues = endContentMarginValues ?: 4.dp.asStatefulValue(), + startPaddingValues = startPaddingValues ?: 10.dp.asStatefulValue(), + endPaddingValues = endPaddingValues ?: 10.dp.asStatefulValue(), ) } } @@ -275,48 +427,76 @@ private class DefaultBadgeDimensions( @Immutable private class DefaultBadgeColors( - override val contentColor: InteractiveColor, - override val backgroundColor: InteractiveColor, - override val labelColor: InteractiveColor, - override val startContentColor: InteractiveColor, - override val endContentColor: InteractiveColor, + override val contentBrush: StatefulValue, + override val backgroundBrush: StatefulValue, + override val labelBrush: StatefulValue, + override val startContentBrush: StatefulValue, + override val endContentBrush: StatefulValue, ) : BadgeColors { + @Deprecated( + "Use contentBrush", + replaceWith = ReplaceWith("contentBrush"), + ) + override val contentColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use backgroundBrush", + replaceWith = ReplaceWith("backgroundBrush"), + ) + override val backgroundColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use labelBrush", + replaceWith = ReplaceWith("labelBrush"), + ) + override val labelColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use startContentBrush", + replaceWith = ReplaceWith("startContentBrush"), + ) + override val startContentColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use endContentBrush", + replaceWith = ReplaceWith("endContentBrush"), + ) + override val endContentColor: InteractiveColor = Color.Transparent.asInteractive() + class Builder : BadgeColorsBuilder { - private var contentColor: InteractiveColor? = null - private var backgroundColor: InteractiveColor? = null - private var labelColor: InteractiveColor? = null - private var startContentColor: InteractiveColor? = null - private var endContentColor: InteractiveColor? = null - override fun contentColor(contentColor: InteractiveColor): BadgeColorsBuilder = apply { - this.contentColor = contentColor + private var contentBrush: StatefulValue? = null + private var backgroundBrush: StatefulValue? = null + private var labelBrush: StatefulValue? = null + private var startContentBrush: StatefulValue? = null + private var endContentBrush: StatefulValue? = null + + override fun backgroundBrush(backgroundBrush: StatefulValue) = apply { + this.backgroundBrush = backgroundBrush } - override fun backgroundColor(backgroundColor: InteractiveColor): BadgeColorsBuilder = - apply { - this.backgroundColor = backgroundColor - } + override fun labelBrush(labelBrush: StatefulValue) = apply { + this.labelBrush = labelBrush + } - override fun labelColor(labelColor: InteractiveColor): BadgeColorsBuilder = apply { - this.labelColor = labelColor + override fun startContentBrush(startContentBrush: StatefulValue) = apply { + this.startContentBrush = startContentBrush } - override fun startContentColor(startContentColor: InteractiveColor): BadgeColorsBuilder = - apply { - this.startContentColor = startContentColor - } + override fun endContentBrush(endContentBrush: StatefulValue) = apply { + this.endContentBrush = endContentBrush + } - override fun endContentColor(endContentColor: InteractiveColor): BadgeColorsBuilder = - apply { - this.endContentColor = endContentColor - } + override fun contentBrush(contentBrush: StatefulValue) = apply { + this.contentBrush = contentBrush + } override fun build(): BadgeColors { return DefaultBadgeColors( - contentColor = contentColor ?: Color.White.asInteractive(), - backgroundColor = backgroundColor ?: Color.Black.asInteractive(), - labelColor = labelColor ?: Color.Black.asInteractive(), - startContentColor = startContentColor ?: Color.Black.asInteractive(), - endContentColor = endContentColor ?: Color.Black.asInteractive(), + contentBrush = contentBrush ?: Color.White.asStatefulBrush(), + backgroundBrush = backgroundBrush ?: Color.Black.asStatefulBrush(), + labelBrush = labelBrush ?: Color.Black.asStatefulBrush(), + startContentBrush = startContentBrush ?: Color.Black.asStatefulBrush(), + endContentBrush = endContentBrush ?: Color.Black.asStatefulBrush(), ) } } @@ -328,6 +508,7 @@ internal class BadgeStyleBuilderImpl(receiver: Any?) : BadgeStyleBuilder { private var labelStyle: TextStyle? = null private var dimensionsBuilder: BadgeDimensionsBuilder = BadgeDimensionsBuilder.builder() private var disableAlpha: Float? = null + private var labelStyles: StatefulValue? = null override fun shape(shape: CornerBasedShape): BadgeStyleBuilder = apply { this.shape = shape @@ -343,8 +524,8 @@ internal class BadgeStyleBuilderImpl(receiver: Any?) : BadgeStyleBuilder { this.colorsBuilder.builder() } - override fun labelStyle(labelStyle: TextStyle): BadgeStyleBuilder = apply { - this.labelStyle = labelStyle + override fun labelStyle(labelStyle: StatefulValue) = apply { + this.labelStyles = labelStyle } @Composable @@ -364,6 +545,7 @@ internal class BadgeStyleBuilderImpl(receiver: Any?) : BadgeStyleBuilder { shape = shape ?: RoundedCornerShape(25), labelStyle = labelStyle ?: TextStyle.Default, disableAlpha = disableAlpha ?: DISABLE_BADGE_ALPHA, + labelStyles = labelStyles ?: TextStyle.Default.asStatefulValue(), ) } } diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyles.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyles.kt index b355960a8a..eb36d5ffd4 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyles.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/BadgeStyles.kt @@ -2,9 +2,11 @@ package com.sdds.compose.uikit import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import com.sdds.compose.uikit.interactions.InteractiveColor +import com.sdds.compose.uikit.interactions.StatefulValue import com.sdds.compose.uikit.style.Style /** @@ -35,8 +37,19 @@ interface BadgeStyle : Style { * Стиль основного текста * @see TextStyle */ + @Deprecated( + "Use labelStyles", + replaceWith = ReplaceWith( + "labelStyles", + ), + ) val labelStyle: TextStyle + /** + * Стиль основного текста + */ + val labelStyles: StatefulValue + /** * Значение прозрачности выключенного компонента */ @@ -55,37 +68,100 @@ interface BadgeDimensions { /** * Высота компонента */ + @Deprecated( + "Use heightValues", + replaceWith = ReplaceWith("heightValues"), + ) val height: Dp + /** + * Высота компонента + */ + val heightValues: StatefulValue + /** * Размер контента в начале */ + @Deprecated( + "Use startContentSizeValues", + replaceWith = ReplaceWith("startContentSizeValues"), + ) val startContentSize: Dp + /** + * Размер контента в начале + */ + val startContentSizeValues: StatefulValue + /** * Размер контента в конце */ + @Deprecated( + "Use endContentSizeValues", + replaceWith = ReplaceWith("endContentSizeValues"), + ) val endContentSize: Dp + /** + * Размер контента в конце + */ + val endContentSizeValues: StatefulValue + /** * Отступы контента в начале */ + @Deprecated( + "Use startContentMarginValues", + replaceWith = ReplaceWith("startContentMarginValues"), + ) val startContentMargin: Dp + /** + * Отступы контента в начале + */ + val startContentMarginValues: StatefulValue + /** * Отступы контента в конце */ + @Deprecated( + "Use endContentMarginValues", + replaceWith = ReplaceWith("endContentMarginValues"), + ) val endContentMargin: Dp + /** + * Отступы контента в конце + */ + val endContentMarginValues: StatefulValue + /** * Отступ от начала компонента до контента */ + @Deprecated( + "Use startPaddingValues", + replaceWith = ReplaceWith("startPaddingValues"), + ) val startPadding: Dp + /** + * Отступ от начала компонента до контента + */ + val startPaddingValues: StatefulValue + /** * Отступ от контента до конца компонента */ + @Deprecated( + "Use endPaddingValues", + replaceWith = ReplaceWith("endPaddingValues"), + ) val endPadding: Dp + + /** + * Отступ от контента до конца компонента + */ + val endPaddingValues: StatefulValue } /** @@ -98,29 +174,74 @@ interface BadgeColors { * Цвет контента * @see InteractiveColor */ + @Deprecated( + "Use contentBrush", + replaceWith = ReplaceWith("contentBrush"), + ) val contentColor: InteractiveColor + /** + * Кисть контента + */ + val contentBrush: StatefulValue + /** * Цвет фона * @see InteractiveColor */ + @Deprecated( + "Use backgroundBrush", + replaceWith = ReplaceWith("backgroundBrush"), + ) val backgroundColor: InteractiveColor + /** + * Кисть фона + */ + val backgroundBrush: StatefulValue + /** * Цвет основного текста * @see InteractiveColor */ + @Deprecated( + "Use labelBrush", + replaceWith = ReplaceWith("labelBrush"), + ) val labelColor: InteractiveColor + /** + * Кисть основного текста + */ + val labelBrush: StatefulValue + /** * Цвет контента в начале * @see InteractiveColor */ + @Deprecated( + "Use startContentBrush", + replaceWith = ReplaceWith("startContentBrush"), + ) val startContentColor: InteractiveColor + /** + * Кисть контента в начале + */ + val startContentBrush: StatefulValue + /** * Цвет контента в конце * @see InteractiveColor */ + @Deprecated( + "Use endContentBrush", + replaceWith = ReplaceWith("endContentBrush"), + ) val endContentColor: InteractiveColor + + /** + * Кисть контента в конце + */ + val endContentBrush: StatefulValue } diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Chip.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Chip.kt index 12bc7d08d6..c8d4371de0 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Chip.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Chip.kt @@ -1,6 +1,7 @@ package com.sdds.compose.uikit import androidx.compose.foundation.Indication +import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.runtime.Composable @@ -42,7 +43,7 @@ fun Chip( val isFocused = interactionSource.collectIsFocusedAsState() val labelStyle = style.labelStyle val dimensionsSet = style.dimensions.toDimensionsSet() - val colorsSet = style.colors.toColorsSet() + val colorsSet = style.colors.toColorsSet(interactionSource) val backgroundColor = style.colors.backgroundColor.colorForInteractionAsState(interactionSource) BaseIconText( modifier = modifier @@ -100,7 +101,7 @@ fun Chip( val isFocused = interactionSource.collectIsFocusedAsState() val labelStyle = style.labelStyle val dimensionsSet = style.dimensions.toDimensionsSet() - val colorsSet = style.colors.toColorsSet() + val colorsSet = style.colors.toColorsSet(interactionSource) val backgroundColor = style.colors.backgroundColor.colorForInteractionAsState(interactionSource) BaseIconText( modifier = modifier @@ -142,12 +143,15 @@ internal fun ChipDimensions.toDimensionsSet(): BaseIconText.Dimensions { ) } -internal fun ChipColors.toColorsSet(): BaseIconText.Colors { - return BaseIconText.Colors( - contentColor = this.contentColor, - labelColor = this.labelColor, - startContentColor = this.contentStartColor, - endContentColor = this.contentEndColor, +@Composable +internal fun ChipColors.toColorsSet( + interactionSource: InteractionSource, +): BaseIconText.Brushes { + return BaseIconText.Brushes( + contentBrush = SolidColor(this.contentColor.colorForInteraction(interactionSource)), + labelBrush = SolidColor(this.labelColor.colorForInteraction(interactionSource)), + startContentBrush = SolidColor(this.contentStartColor.colorForInteraction(interactionSource)), + endContentBrush = SolidColor(this.contentEndColor.colorForInteraction(interactionSource)), ) } diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/IconBadgeStyleBuilder.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/IconBadgeStyleBuilder.kt index 9fe73396a3..1608423d10 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/IconBadgeStyleBuilder.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/IconBadgeStyleBuilder.kt @@ -5,12 +5,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.sdds.compose.uikit.graphics.brush.asStatefulBrush import com.sdds.compose.uikit.interactions.InteractiveColor +import com.sdds.compose.uikit.interactions.StatefulValue import com.sdds.compose.uikit.interactions.asInteractive +import com.sdds.compose.uikit.interactions.asStatefulBrush +import com.sdds.compose.uikit.interactions.asStatefulValue import com.sdds.compose.uikit.style.StyleBuilder /** @@ -58,22 +63,46 @@ interface IconBadgeDimensionsBuilder { /** * Устанавливает высоту Badge */ - fun height(height: Dp): IconBadgeDimensionsBuilder + fun height(height: Dp): IconBadgeDimensionsBuilder = + height(height.asStatefulValue()) + + /** + * Устанавливает высоту Badge + */ + fun height(values: StatefulValue): IconBadgeDimensionsBuilder /** * Устанавливает размер иконки в начале */ - fun startContentSize(startContentSize: Dp): IconBadgeDimensionsBuilder + fun startContentSize(startContentSize: Dp): IconBadgeDimensionsBuilder = + startContentSize(startContentSize.asStatefulValue()) + + /** + * Устанавливает размер иконки в начале + */ + fun startContentSize(values: StatefulValue): IconBadgeDimensionsBuilder + + /** + * Устанавливает отступ от начала компонента до контента + */ + fun startPadding(startPadding: Dp): IconBadgeDimensionsBuilder = + startPadding(startPadding.asStatefulValue()) /** * Устанавливает отступ от начала компонента до контента */ - fun startPadding(startPadding: Dp): IconBadgeDimensionsBuilder + fun startPadding(values: StatefulValue): IconBadgeDimensionsBuilder /** * Устанавливает отступ от контента до конца компонента */ - fun endPadding(endPadding: Dp): IconBadgeDimensionsBuilder + fun endPadding(endPadding: Dp): IconBadgeDimensionsBuilder = + endPadding(endPadding.asStatefulValue()) + + /** + * Устанавливает отступ от контента до конца компонента + */ + fun endPadding(values: StatefulValue): IconBadgeDimensionsBuilder /** * Возвращает готовый экземпляр [BadgeDimensions] @@ -98,29 +127,52 @@ interface IconBadgeColorsBuilder { * @see BadgeColors.backgroundColor * @see InteractiveColor */ - fun backgroundColor(backgroundColor: InteractiveColor): IconBadgeColorsBuilder + fun backgroundColor(backgroundColor: InteractiveColor): IconBadgeColorsBuilder = + backgroundBrush(backgroundColor.asStatefulBrush()) /** * Устанавливает цвет фона компонента [backgroundColor] * @see BadgeColors.backgroundColor */ fun backgroundColor(backgroundColor: Color): IconBadgeColorsBuilder = - backgroundColor(backgroundColor.asInteractive()) + backgroundBrush(backgroundColor.asStatefulBrush()) /** - * Устанавливает цвет дополнительного текста компонента [startContentColor] + * Устанавливает цвет фона компонента [backgroundColor] + */ + fun backgroundColor(backgroundColor: StatefulValue): IconBadgeColorsBuilder = + backgroundBrush(backgroundColor.asStatefulBrush()) + + /** + * Устанавливает кисть фона компонента [backgroundBrush] + */ + fun backgroundBrush(backgroundBrush: StatefulValue): IconBadgeColorsBuilder + + /** + * Устанавливает цвет контента [startContentColor] * @see BadgeColors.startContentColor * @see InteractiveColor */ - fun startContentColor(startContentColor: InteractiveColor): IconBadgeColorsBuilder + fun startContentColor(startContentColor: InteractiveColor): IconBadgeColorsBuilder = + startContentBrush(startContentColor.asStatefulBrush()) /** - * Устанавливает цвет дополнительного текста компонента [valueColor] + * Устанавливает цвет контента [valueColor] * @see BadgeColors.startContentColor - * @see InteractiveColor */ fun startContentColor(valueColor: Color): IconBadgeColorsBuilder = - startContentColor(valueColor.asInteractive()) + startContentBrush(valueColor.asStatefulBrush()) + + /** + * Устанавливает цвет контента [startContentColor] + */ + fun startContentColor(startContentColor: StatefulValue): IconBadgeColorsBuilder = + startContentBrush(startContentColor.asStatefulBrush()) + + /** + * Устанавливает кисть контента [startContentBrush] + */ + fun startContentBrush(startContentBrush: StatefulValue): IconBadgeColorsBuilder /** * Возвращает готовый экземпляр [BadgeColors] @@ -143,52 +195,96 @@ private class DefaultIconBadgeStyle( override val shape: CornerBasedShape, override val labelStyle: TextStyle, override val disableAlpha: Float, + override val labelStyles: StatefulValue, ) : BadgeStyle @Immutable private class DefaultIconBadgeDimensions( - override val height: Dp, - override val startContentSize: Dp, - override val endContentSize: Dp, - override val startContentMargin: Dp, - override val endContentMargin: Dp, - override val startPadding: Dp, - override val endPadding: Dp, + override val heightValues: StatefulValue, + override val startContentSizeValues: StatefulValue, + override val endContentSizeValues: StatefulValue, + override val startContentMarginValues: StatefulValue, + override val endContentMarginValues: StatefulValue, + override val startPaddingValues: StatefulValue, + override val endPaddingValues: StatefulValue, ) : BadgeDimensions { + @Deprecated( + "Use heightValues", + replaceWith = ReplaceWith("heightValues"), + ) + override val height: Dp = 28.dp + + @Deprecated( + "Use startContentSizeValues", + replaceWith = ReplaceWith("startContentSizeValues"), + ) + override val startContentSize: Dp = 16.dp + + @Deprecated( + "Use endContentSizeValues", + replaceWith = ReplaceWith("endContentSizeValues"), + ) + override val endContentSize: Dp = 0.dp + + @Deprecated( + "Use startContentMarginValues", + replaceWith = ReplaceWith("startContentMarginValues"), + ) + override val startContentMargin: Dp = 0.dp + + @Deprecated( + "Use endContentMarginValues", + replaceWith = ReplaceWith("endContentMarginValues"), + ) + override val endContentMargin: Dp = 0.dp + + @Deprecated( + "Use startPaddingValues", + replaceWith = ReplaceWith("startPaddingValues"), + ) + override val startPadding: Dp = 6.dp + + @Deprecated( + "Use endPaddingValues", + replaceWith = ReplaceWith("endPaddingValues"), + ) + override val endPadding: Dp = 6.dp + class Builder : IconBadgeDimensionsBuilder { - private var height: Dp? = null - private var endContentSize: Dp? = null - private var startContentSize: Dp? = null - private var startContentMargin: Dp? = null - private var endContentMargin: Dp? = null - private var startPadding: Dp? = null - private var endPadding: Dp? = null - override fun height(height: Dp): IconBadgeDimensionsBuilder = apply { - this.height = height + private var heightValues: StatefulValue? = null + private var endContentSizeValues: StatefulValue? = null + private var startContentSizeValues: StatefulValue? = null + private var startContentMarginValues: StatefulValue? = null + private var endContentMarginValues: StatefulValue? = null + private var startPaddingValues: StatefulValue? = null + private var endPaddingValues: StatefulValue? = null + + override fun height(values: StatefulValue): IconBadgeDimensionsBuilder = apply { + this.heightValues = values } - override fun startContentSize(startContentSize: Dp): IconBadgeDimensionsBuilder = apply { - this.startContentSize = startContentSize + override fun startContentSize(values: StatefulValue): IconBadgeDimensionsBuilder = apply { + this.startContentSizeValues = values } - override fun startPadding(startPadding: Dp): IconBadgeDimensionsBuilder = apply { - this.startPadding = startPadding + override fun startPadding(values: StatefulValue): IconBadgeDimensionsBuilder = apply { + this.startPaddingValues = values } - override fun endPadding(endPadding: Dp): IconBadgeDimensionsBuilder = apply { - this.endPadding = endPadding + override fun endPadding(values: StatefulValue): IconBadgeDimensionsBuilder = apply { + this.endPaddingValues = values } override fun build(): BadgeDimensions { return DefaultIconBadgeDimensions( - height = height ?: 28.dp, - startContentSize = startContentSize ?: 16.dp, - endContentSize = endContentSize ?: 0.dp, - startContentMargin = startContentMargin ?: 0.dp, - endContentMargin = endContentMargin ?: 0.dp, - startPadding = startPadding ?: 6.dp, - endPadding = endPadding ?: 6.dp, + heightValues = heightValues ?: 28.dp.asStatefulValue(), + startContentSizeValues = startContentSizeValues ?: 16.dp.asStatefulValue(), + endContentSizeValues = endContentSizeValues ?: 0.dp.asStatefulValue(), + startContentMarginValues = startContentMarginValues ?: 0.dp.asStatefulValue(), + endContentMarginValues = endContentMarginValues ?: 0.dp.asStatefulValue(), + startPaddingValues = startPaddingValues ?: 6.dp.asStatefulValue(), + endPaddingValues = endPaddingValues ?: 6.dp.asStatefulValue(), ) } } @@ -196,36 +292,64 @@ private class DefaultIconBadgeDimensions( @Immutable private class DefaultIconBadgeColors( - override val contentColor: InteractiveColor, - override val backgroundColor: InteractiveColor, - override val labelColor: InteractiveColor, - override val startContentColor: InteractiveColor, - override val endContentColor: InteractiveColor, + override val contentBrush: StatefulValue, + override val backgroundBrush: StatefulValue, + override val labelBrush: StatefulValue, + override val startContentBrush: StatefulValue, + override val endContentBrush: StatefulValue, ) : BadgeColors { + @Deprecated( + "Use contentBrush", + replaceWith = ReplaceWith("contentBrush"), + ) + override val contentColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use backgroundBrush", + replaceWith = ReplaceWith("backgroundBrush"), + ) + override val backgroundColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use labelBrush", + replaceWith = ReplaceWith("labelBrush"), + ) + override val labelColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use startContentBrush", + replaceWith = ReplaceWith("startContentBrush"), + ) + override val startContentColor: InteractiveColor = Color.Transparent.asInteractive() + + @Deprecated( + "Use endContentBrush", + replaceWith = ReplaceWith("endContentBrush"), + ) + override val endContentColor: InteractiveColor = Color.Transparent.asInteractive() + class Builder : IconBadgeColorsBuilder { - private var contentColor: InteractiveColor? = null - private var backgroundColor: InteractiveColor? = null - private var labelColor: InteractiveColor? = null - private var startContentColor: InteractiveColor? = null - private var endContentColor: InteractiveColor? = null - - override fun backgroundColor(backgroundColor: InteractiveColor): IconBadgeColorsBuilder = - apply { - this.backgroundColor = backgroundColor - } - - override fun startContentColor(startContentColor: InteractiveColor): IconBadgeColorsBuilder = - apply { - this.startContentColor = startContentColor - } + private var contentBrush: StatefulValue? = null + private var backgroundBrush: StatefulValue? = null + private var labelBrush: StatefulValue? = null + private var startContentBrush: StatefulValue? = null + private var endContentBrush: StatefulValue? = null + + override fun backgroundBrush(backgroundBrush: StatefulValue): IconBadgeColorsBuilder = apply { + this.backgroundBrush = backgroundBrush + } + + override fun startContentBrush(startContentBrush: StatefulValue): IconBadgeColorsBuilder = apply { + this.startContentBrush = startContentBrush + } override fun build(): BadgeColors { return DefaultIconBadgeColors( - contentColor = contentColor ?: Color.White.asInteractive(), - backgroundColor = backgroundColor ?: Color.Black.asInteractive(), - labelColor = labelColor ?: Color.Black.asInteractive(), - startContentColor = startContentColor ?: Color.Black.asInteractive(), - endContentColor = endContentColor ?: Color.Black.asInteractive(), + contentBrush = contentBrush ?: Color.White.asStatefulBrush(), + backgroundBrush = backgroundBrush ?: Color.Black.asStatefulBrush(), + labelBrush = labelBrush ?: Color.Black.asStatefulBrush(), + startContentBrush = startContentBrush ?: Color.Black.asStatefulBrush(), + endContentBrush = endContentBrush ?: Color.Black.asStatefulBrush(), ) } } @@ -237,6 +361,7 @@ private class IconBadgeStyleBuilderImpl(receiver: Any?) : IconBadgeStyleBuilder private var labelStyle: TextStyle? = null private var dimensionsBuilder: IconBadgeDimensionsBuilder = IconBadgeDimensionsBuilder.builder() private var disableAlpha: Float? = null + private var labelStyles: StatefulValue? = null override fun shape(shape: CornerBasedShape): IconBadgeStyleBuilder = apply { this.shape = shape @@ -269,6 +394,7 @@ private class IconBadgeStyleBuilderImpl(receiver: Any?) : IconBadgeStyleBuilder shape = shape ?: RoundedCornerShape(25), labelStyle = labelStyle ?: TextStyle.Default, disableAlpha = disableAlpha ?: DISABLE_BADGE_ALPHA, + labelStyles = labelStyles ?: TextStyle.Default.asStatefulValue(), ) } } diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Toast.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Toast.kt index 2a1486f562..d91a8299ff 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Toast.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/Toast.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.sdds.compose.uikit.interactions.getValue @@ -81,10 +82,10 @@ fun Toast( endPadding = dimensions.paddingEnd, ), labelStyle = style.textStyle, - colorsSet = BaseIconText.Colors( - labelColor = colors.textColor, - startContentColor = colors.contentStartColor, - endContentColor = colors.contentEndColor, + colorsSet = BaseIconText.Brushes( + labelBrush = SolidColor(colors.textColor.colorForInteraction(interactionSource)), + startContentBrush = SolidColor(colors.contentStartColor.colorForInteraction(interactionSource)), + endContentBrush = SolidColor(colors.contentEndColor.colorForInteraction(interactionSource)), ), startContent = contentStart, endContent = contentEnd, diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/internal/icontext/BaseIconText.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/internal/icontext/BaseIconText.kt index 51f1f1382e..e4033fef00 100644 --- a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/internal/icontext/BaseIconText.kt +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/internal/icontext/BaseIconText.kt @@ -11,7 +11,9 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasurePolicy @@ -28,7 +30,7 @@ import androidx.compose.ui.unit.constrainHeight import androidx.compose.ui.unit.constrainWidth import androidx.compose.ui.unit.dp import com.sdds.compose.uikit.LocalIconDefaultSize -import com.sdds.compose.uikit.LocalTint +import com.sdds.compose.uikit.LocalTintBrushProducer import com.sdds.compose.uikit.ProvideTextStyle import com.sdds.compose.uikit.Text import com.sdds.compose.uikit.interactions.InteractiveColor @@ -47,7 +49,7 @@ import com.sdds.compose.uikit.interactions.getValueAsState internal fun BaseIconText( modifier: Modifier = Modifier, dimensionsSet: BaseIconText.Dimensions, - colorsSet: BaseIconText.Colors, + colorsSet: BaseIconText.Brushes, labelContent: String = "", labelStyle: TextStyle, startContent: (@Composable () -> Unit)? = null, @@ -64,13 +66,13 @@ internal fun BaseIconText( ), measurePolicy = measurePolicy, content = { - val contentColor = colorsSet.contentColor.colorForInteraction(interactionSource) + val contentColor = colorsSet.contentBrush val startContentColor = - colorsSet.startContentColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.startContentBrush ?: contentColor val endContentColor = - colorsSet.endContentColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.endContentBrush ?: contentColor val labelColor = - colorsSet.labelColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.labelBrush ?: contentColor IconTextContent( dimensions = dimensionsSet, startContent = startContent, @@ -94,7 +96,7 @@ internal fun BaseIconText( internal fun BaseIconText( modifier: Modifier = Modifier, dimensionsSet: BaseIconText.Dimensions, - colorsSet: BaseIconText.Colors, + colorsSet: BaseIconText.Brushes, labelStyle: TextStyle = TextStyle.Default, labelContent: (@Composable () -> Unit)? = null, startContent: (@Composable () -> Unit)? = null, @@ -111,13 +113,13 @@ internal fun BaseIconText( ), measurePolicy = measurePolicy, content = { - val contentColor = colorsSet.contentColor.colorForInteraction(interactionSource) + val contentColor = colorsSet.contentBrush val startContentColor = - colorsSet.startContentColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.startContentBrush ?: contentColor val endContentColor = - colorsSet.endContentColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.endContentBrush ?: contentColor val labelColor = - colorsSet.labelColor?.colorForInteraction(interactionSource) ?: contentColor + colorsSet.labelBrush ?: contentColor IconTextContent( dimensions = dimensionsSet, startContent = startContent, @@ -196,16 +198,16 @@ private class IconTextMeasurePolicy : MeasurePolicy { private fun IconTextContent( startContent: (@Composable () -> Unit)?, endContent: (@Composable () -> Unit)?, - startContentColor: Color, - endContentColor: Color, - labelColor: Color, + startContentColor: Brush, + endContentColor: Brush, + labelColor: Brush, label: String, labelStyle: TextStyle, dimensions: BaseIconText.Dimensions, ) { startContent?.let { CompositionLocalProvider( - LocalTint provides startContentColor, + LocalTintBrushProducer provides { startContentColor }, ) { val startSpacing = remember(label, dimensions) { dimensions.startContentMargin.takeIf { label.isNotEmpty() } ?: 0.dp @@ -225,14 +227,14 @@ private fun IconTextContent( Text( modifier = Modifier.layoutId(TEXT_CONTENT), text = label, - style = labelStyle.copy(color = labelColor), + style = labelStyle.copy(brush = labelColor), maxLines = 1, overflow = TextOverflow.Ellipsis, ) } endContent?.let { CompositionLocalProvider( - LocalTint provides endContentColor, + LocalTintBrushProducer provides { endContentColor }, ) { val endSpacing = remember(label, dimensions) { dimensions.endContentMargin.takeIf { label.isNotEmpty() } ?: 0.dp @@ -254,16 +256,16 @@ private fun IconTextContent( private fun IconTextContent( startContent: (@Composable () -> Unit)?, endContent: (@Composable () -> Unit)?, - startContentColor: Color, - endContentColor: Color, - labelColor: Color, + startContentColor: Brush, + endContentColor: Brush, + labelColor: Brush, labelStyle: TextStyle, labelContent: (@Composable () -> Unit)?, dimensions: BaseIconText.Dimensions, ) { startContent?.let { CompositionLocalProvider( - LocalTint provides startContentColor, + LocalTintBrushProducer provides { startContentColor }, LocalIconDefaultSize provides dimensions.startContentSize.let { DpSize(it, it) }, ) { Box( @@ -276,7 +278,7 @@ private fun IconTextContent( } } labelContent?.let { - ProvideTextStyle(labelStyle, color = { labelColor }) { + ProvideTextStyle(labelStyle, brush = { labelColor }) { Box( modifier = Modifier .layoutId(TEXT_CONTENT), @@ -285,7 +287,7 @@ private fun IconTextContent( } endContent?.let { CompositionLocalProvider( - LocalTint provides endContentColor, + LocalTintBrushProducer provides { endContentColor }, LocalIconDefaultSize provides dimensions.endContentSize.let { DpSize(it, it) }, ) { Box( @@ -360,6 +362,22 @@ internal object BaseIconText { val startContentColor: InteractiveColor? = contentColor, val endContentColor: InteractiveColor? = contentColor, ) + + /** + * Кисти, которые используются внутри компонента. + * + * @property contentBrush кисть контента + * @property labelBrush кисть текста + * @property startContentBrush кисть контента в начале компонента + * @property endContentBrush кисть контента в конце компонента + */ + @Immutable + data class Brushes( + val contentBrush: Brush = SolidColor(Color.Black), + val labelBrush: Brush? = contentBrush, + val startContentBrush: Brush? = contentBrush, + val endContentBrush: Brush? = contentBrush, + ) } private fun Placeable?.widthOrZero(): Int { diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotion.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotion.kt new file mode 100644 index 0000000000..e7421105e3 --- /dev/null +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotion.kt @@ -0,0 +1,22 @@ +package com.sdds.compose.uikit.motion.components.badge + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.MotionContext +import com.sdds.compose.uikit.motion.rememberMotion +import com.sdds.compose.uikit.motion.rememberMotionContext + +/** + * Создает [Motion] для [Badge] + * @param motionContext контекст анимации + * @param style стиль анимации [Badge] + */ +@Composable +@NonRestartableComposable +fun rememberBadgeMotion( + style: BadgeMotionStyle = LocalBadgeMotionStyle.current, + motionContext: MotionContext = rememberMotionContext(), +): Motion { + return rememberMotion(style, motionContext) +} diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotionStyle.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotionStyle.kt new file mode 100644 index 0000000000..737f2d747f --- /dev/null +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/BadgeMotionStyle.kt @@ -0,0 +1,155 @@ +package com.sdds.compose.uikit.motion.components.badge + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.text.TextStyle +import com.sdds.compose.uikit.motion.MotionProperty +import com.sdds.compose.uikit.motion.MotionStyle +import com.sdds.compose.uikit.motion.MotionStyleBuilder +import com.sdds.compose.uikit.motion.noMotion + +/** + * CompositionLocal, предоставляющий текущий [BadgeMotionStyle]. + * Используется для доступа к анимационным свойствам Badge внутри Compose-иерархии. + */ +val LocalBadgeMotionStyle = compositionLocalOf { BadgeMotionStyle.builder().style() } + +/** + * Описывает анимационные (motion) свойства для элемента Badge. + * Содержит набор [MotionProperty], определяющих поведение цветов и вложенных компонентов + * при различных состояниях (например, выбран, нажат и т.д.). + */ +@Stable +interface BadgeMotionStyle : MotionStyle { + + /** + * Анимационное свойство цвета фона Badge. + */ + val backgroundColor: MotionProperty + + /** + * Анимационное свойство цвета основного текста (label) Badge. + */ + val labelColor: MotionProperty + + /** + * Анимационное свойство стиля основного текста (label) Badge. + */ + val labelStyle: MotionProperty + + /** + * Анимационное свойство цвета контента (как в начале, так и в конце) Badge. + */ + val contentColor: MotionProperty + + /** + * Анимационное свойство цвета контента, расположенного в начале Badge. + */ + val startContentColor: MotionProperty + + /** + * Анимационное свойство цвета контента, расположенного в конце Badge. + */ + val endContentColor: MotionProperty + + companion object { + /** + * Создает билдер для построения [BadgeMotionStyle]. + */ + fun builder(): BadgeMotionStyleBuilder = BadgeMotionStyleImpl.Builder() + } +} + +/** + * Билдер для поэтапной конфигурации [BadgeMotionStyle]. + */ +@Stable +interface BadgeMotionStyleBuilder : MotionStyleBuilder { + + /** + * Устанавливает анимационное свойство цвета фона. + */ + fun backgroundColor(background: MotionProperty): BadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство цвета label. + */ + fun labelColor(label: MotionProperty): BadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство стиля label. + */ + fun labelStyle(label: MotionProperty): BadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство цвета начального контента. + */ + fun contentColor(content: MotionProperty): BadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство цвета начального контента. + */ + fun startContentColor(startContent: MotionProperty): BadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство цвета конечного контента. + */ + fun endContentColor(endContent: MotionProperty): BadgeMotionStyleBuilder +} + +@Immutable +private class BadgeMotionStyleImpl( + override val backgroundColor: MotionProperty, + override val labelColor: MotionProperty, + override val contentColor: MotionProperty, + override val startContentColor: MotionProperty, + override val endContentColor: MotionProperty, + override val labelStyle: MotionProperty, +) : BadgeMotionStyle { + + class Builder : BadgeMotionStyleBuilder { + private var background: MotionProperty? = null + private var label: MotionProperty? = null + private var content: MotionProperty? = null + private var startContent: MotionProperty? = null + private var endContent: MotionProperty? = null + private var labelStyle: MotionProperty? = null + + override fun backgroundColor(background: MotionProperty) = apply { + this.background = background + } + + override fun labelColor(label: MotionProperty) = apply { + this.label = label + } + + override fun labelStyle(label: MotionProperty) = apply { + this.labelStyle = label + } + + override fun contentColor(content: MotionProperty) = apply { + this.content = content + } + + override fun startContentColor(startContent: MotionProperty) = apply { + this.startContent = startContent + } + + override fun endContentColor(endContent: MotionProperty) = apply { + this.endContent = endContent + } + + override fun style(): BadgeMotionStyle { + return BadgeMotionStyleImpl( + backgroundColor = background ?: noMotion(), + labelColor = label ?: noMotion(), + contentColor = content ?: noMotion(), + startContentColor = startContent ?: noMotion(), + endContentColor = endContent ?: noMotion(), + labelStyle = labelStyle ?: noMotion(), + ) + } + } +} diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotion.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotion.kt new file mode 100644 index 0000000000..e271f8e347 --- /dev/null +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotion.kt @@ -0,0 +1,22 @@ +package com.sdds.compose.uikit.motion.components.badge + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.NonRestartableComposable +import com.sdds.compose.uikit.motion.Motion +import com.sdds.compose.uikit.motion.MotionContext +import com.sdds.compose.uikit.motion.rememberMotion +import com.sdds.compose.uikit.motion.rememberMotionContext + +/** + * Создает [Motion] для [IconBadge] + * @param motionContext контекст анимации + * @param style стиль анимации [IconBadge] + */ +@Composable +@NonRestartableComposable +fun rememberIconBadgeMotion( + style: IconBadgeMotionStyle = LocalIconBadgeMotionStyle.current, + motionContext: MotionContext = rememberMotionContext(), +): Motion { + return rememberMotion(style, motionContext) +} diff --git a/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotionStyle.kt b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotionStyle.kt new file mode 100644 index 0000000000..3428e0b057 --- /dev/null +++ b/sdds-core/uikit-compose/src/main/kotlin/com/sdds/compose/uikit/motion/components/badge/IconBadgeMotionStyle.kt @@ -0,0 +1,86 @@ +package com.sdds.compose.uikit.motion.components.badge + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Brush +import com.sdds.compose.uikit.motion.MotionProperty +import com.sdds.compose.uikit.motion.MotionStyle +import com.sdds.compose.uikit.motion.MotionStyleBuilder +import com.sdds.compose.uikit.motion.noMotion + +/** + * CompositionLocal, предоставляющий текущий [IconBadgeMotionStyle]. + * Используется для доступа к анимационным свойствам IconBadge внутри Compose-иерархии. + */ +val LocalIconBadgeMotionStyle = compositionLocalOf { IconBadgeMotionStyle.builder().style() } + +/** + * Описывает анимационные (motion) свойства для элемента IconBadge. + * Содержит набор [MotionProperty], определяющих поведение цветов и вложенных компонентов + * при различных состояниях (например, выбран, нажат и т.д.). + */ +@Stable +interface IconBadgeMotionStyle : MotionStyle { + + /** + * Анимационное свойство цвета фона IconBadge. + */ + val backgroundColor: MotionProperty + + /** + * Анимационное свойство цвета контента, расположенного в начале IconBadge. + */ + val startContentColor: MotionProperty + + companion object { + /** + * Создает билдер для построения [IconBadgeMotionStyle]. + */ + fun builder(): IconBadgeMotionStyleBuilder = IconBadgeMotionStyleImpl.Builder() + } +} + +/** + * Билдер для поэтапной конфигурации [IconBadgeMotionStyle]. + */ +@Stable +interface IconBadgeMotionStyleBuilder : MotionStyleBuilder { + + /** + * Устанавливает анимационное свойство цвета фона. + */ + fun backgroundColor(background: MotionProperty): IconBadgeMotionStyleBuilder + + /** + * Устанавливает анимационное свойство цвета начального контента. + */ + fun startContentColor(startContent: MotionProperty): IconBadgeMotionStyleBuilder +} + +@Immutable +private class IconBadgeMotionStyleImpl( + override val backgroundColor: MotionProperty, + override val startContentColor: MotionProperty, +) : IconBadgeMotionStyle { + + class Builder : IconBadgeMotionStyleBuilder { + private var background: MotionProperty? = null + private var startContent: MotionProperty? = null + + override fun backgroundColor(background: MotionProperty) = apply { + this.background = background + } + + override fun startContentColor(startContent: MotionProperty) = apply { + this.startContent = startContent + } + + override fun style(): IconBadgeMotionStyle { + return IconBadgeMotionStyleImpl( + backgroundColor = background ?: noMotion(), + startContentColor = startContent ?: noMotion(), + ) + } + } +}