From 9991cbc59654cac67b9981d2af364b41016bfabd Mon Sep 17 00:00:00 2001 From: Ji Sungbin Date: Tue, 9 May 2023 14:28:06 +0900 Subject: [PATCH] Add a missing button style tokens + relnote (#676) - Add a missing button style tokens - Prepare version 2.0.0-alpha02 --- .../quackquack/runtime/materializing.kt | 2 + .../team/duckie/quackquack/runtime/model.kt | 2 +- .../quackquack/ui/component/ButtonTest.kt | 2 +- .../team/duckie/quackquack/ui/button.kt | 480 ++++++++++++---- .../team/duckie/quackquack/ui/sugar/button.kt | 529 ++++++++++++++++-- website/docs/releases.mdx | 17 +- 6 files changed, 895 insertions(+), 137 deletions(-) diff --git a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt index 474eda847..d9a1719e7 100644 --- a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt +++ b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt @@ -22,6 +22,7 @@ import team.duckie.quackquack.util.MustBeTested * * @param composeModifier `androidx.compose.ui.Modifier`으로만 구성된 [Modifier] * @param quackDataModels [QuackDataModifierModel]으로만 구성된 리스트 + * * @see Composer.quackMaterializeOf */ @NoCopy @@ -49,6 +50,7 @@ internal object QuackMaterializingErrors { * @param modifier 분석할 [Modifier] * @param taversingCallback 주어진 [Modifier]를 foldIn으로 순회하며 방문하는 element마다 * 호출할 선택적 콜백 + * * @return 컴포즈 자체의 [Modifier]와 [QuackDataModifierModel] 리스트를 담은 클래스 */ @MustBeTested(passed = true) diff --git a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt index 719034d9c..093484a20 100644 --- a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt +++ b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier * * @Stable * fun Modifier.appendStringData(data: String): Modifier { - * return then(StringData(data)) + * return then(StringData(data)) * } * ``` * diff --git a/ui/src/androidTest/kotlin/team/duckie/quackquack/ui/component/ButtonTest.kt b/ui/src/androidTest/kotlin/team/duckie/quackquack/ui/component/ButtonTest.kt index cc94484fd..22b8f1a8d 100644 --- a/ui/src/androidTest/kotlin/team/duckie/quackquack/ui/component/ButtonTest.kt +++ b/ui/src/androidTest/kotlin/team/duckie/quackquack/ui/component/ButtonTest.kt @@ -69,7 +69,7 @@ class ButtonTest { rule.setQuackContent { QuackButton( enabled = false, - style = QuackButtonStyle.Large, + style = QuackButtonStyle.PrimaryLarge, text = "button", onClick = onClick, ) diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt index ebdf47b6d..9c85c4cbd 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/button.kt @@ -69,6 +69,16 @@ import team.duckie.quackquack.util.fastFirstIsInstanceOrNull @QuackDsl public interface ButtonStyleMarker +/** Large Button 디자인 스펙을 나타냅니다. */ +public interface QuackLargeButtonStyle : ButtonStyleMarker + +/** Medium Button 디자인 스펙을 나타냅니다. */ +public interface QuackMediumButtonStyle : ButtonStyleMarker + +// TODO(3): 데코레이터 사용 불가능 린트 제공 +/** Small Button 디자인 스펙을 나타냅니다. */ +public interface QuackSmallButtonStyle : ButtonStyleMarker + /** 기본으로 제공되는 [ButtonStyleMarker]의 스펙들에서 공통되는 필드를 나타냅니다. */ @Immutable public interface QuackButtonStyle { @@ -93,23 +103,53 @@ public interface QuackButtonStyle { /** 비활성화 상태에서 표시될 텍스트의 타이포그래피 */ public val disabledTypography: QuackTypography - /** 필드에 mutation을 적용하는 람다 */ + /** 디자인 스펙을 변경하는 람다 */ @Stable public operator fun invoke(styleBuilder: T.() -> Unit): T + // TODO: 이 부분까지 자동화는 무리일까? + // TODO: 더 효율적인 토큰화 방식 고안... + // 지금은 중복되는 코드가 많이 나온다. 얘도 Modifier로 처리할까? 근데 어떤 스타일로? public companion object { - /** 기본으로 정의된 스펙 중 [QuackLargeButtonDefaults]를 가져옵니다. */ + /** 버튼 디자인 가이드의 `LargeButtons # primary` 디자인 스펙을 가져옵니다. */ + @Stable + public val PrimaryLarge: QuackButtonStyle + get() = QuackPrimaryLargeButtonDefaults() + + /** 버튼 디자인 가이드의 `LargeButtons # secondary` 디자인 스펙을 가져옵니다. */ + @Stable + public val SecondaryLarge: QuackButtonStyle + get() = QuackSecondaryLargeButtonDefaults() + + /** 버튼 디자인 가이드의 `MediumButton` 디자인 스펙을 가져옵니다. */ @Stable - public val Large: QuackButtonStyle get() = QuackLargeButtonDefaults() + public val Medium: QuackButtonStyle + get() = QuackMediumButtonDefaults() - /** 기본으로 정의된 스펙 중 [QuackMediumButtonDefaults]를 가져옵니다. */ + /** 버튼 디자인 가이드의 `SmallButtons # primary, filled` 디자인 스펙을 가져옵니다. */ @Stable - public val Medium: QuackButtonStyle get() = QuackMediumButtonDefaults() + public val PrimaryFilledSmall: QuackButtonStyle + get() = QuackPrimaryFilledSmallButtonDefaults() - /** 기본으로 정의된 스펙 중 [QuackSmallButtonDefaults]를 가져옵니다. */ - // TODO(3): 데코레이터 모디파이어 사용 금지 린트 제공 + /** 버튼 디자인 가이드의 `SmallButtons # primary, outlined` 디자인 스펙을 가져옵니다. */ @Stable - public val Small: QuackButtonStyle get() = QuackSmallButtonDefaults() + public val PrimaryOutlinedSmall: QuackButtonStyle + get() = QuackPrimaryOutlinedSmallButtonDefaults() + + /** 버튼 디자인 가이드의 `SmallButtons # primary, outlined, round` 디자인 스펙을 가져옵니다. */ + @Stable + public val PrimaryOutlinedRoundSmall: QuackButtonStyle + get() = QuackPrimaryOutlinedRoundSmallButtonDefaults() + + /** 버튼 디자인 가이드의 `SmallButtons # secondary` 디자인 스펙을 가져옵니다. */ + @Stable + public val SecondarySmall: QuackButtonStyle + get() = QuackSecondarySmallButtonDefaults() + + /** 버튼 디자인 가이드의 `SmallButtons # secondary, round` 디자인 스펙을 가져옵니다. */ + @Stable + public val SecondaryRoundSmall: QuackButtonStyle + get() = QuackSecondaryRoundSmallButtonDefaults() } } @@ -153,7 +193,7 @@ public class QuackButtonColors internal constructor( ) { /** 기존 색상에서 일부 값만 변경하여 새로운 인스턴스를 반환합니다. */ @Stable - internal fun copy( + public fun copy( backgroundColor: QuackColor = this.backgroundColor, disabledBackgroundColor: QuackColor = this.disabledBackgroundColor, contentColor: QuackColor = this.contentColor, @@ -162,8 +202,8 @@ public class QuackButtonColors internal constructor( disabledBorderColor: QuackColor = this.disabledBorderColor, iconColor: QuackColor = this.iconColor, rippleColor: QuackColor = this.rippleColor, - ): QuackButtonColors { - return QuackButtonColors( + ): QuackButtonColors = + QuackButtonColors( backgroundColor = backgroundColor, disabledBackgroundColor = disabledBackgroundColor, contentColor = contentColor, @@ -173,7 +213,6 @@ public class QuackButtonColors internal constructor( iconColor = iconColor, rippleColor = rippleColor, ) - } @Suppress("RedundantIf") override fun equals(other: Any?): Boolean { @@ -218,32 +257,25 @@ public class QuackButtonColors internal constructor( } } -/** - * 꽥꽥 디자인 가이드의 `Buttons#LargeButtons`에 해당하는 디자인 스펙을 정의합니다. - * - * ### mutation 가능한 필드 - * - * - colors(backgroundColor, disabledBackgroundColor, contentColor, - * disabledContentColor, borderColor, disabledBorderColor, iconColor) - */ -@Immutable -public class QuackLargeButtonDefaults internal constructor() : - QuackButtonStyle, ButtonStyleMarker { +/** 버튼 디자인 가이드의 `LargeButtons # primary` 디자인 스펙을 정의합니다. */ +@Stable +public class QuackPrimaryLargeButtonDefaults internal constructor() : + QuackButtonStyle, QuackLargeButtonStyle { override var colors: QuackButtonColors = buttonColors() - override val radius: Dp = 8.dp + override var radius: Dp = 8.dp - override val contentPadding: QuackPadding = QuackPadding( + override var contentPadding: QuackPadding = QuackPadding( horizontal = 112.dp, vertical = 12.dp, ) - override val iconSpacedBy: Dp = 4.dp + override var iconSpacedBy: Dp = 4.dp - override val borderThickness: Dp = 1.dp + override var borderThickness: Dp = 1.dp - override val typography: QuackTypography = QuackTypography.Subtitle - override val disabledTypography: QuackTypography = typography + override var typography: QuackTypography = QuackTypography.Subtitle + override var disabledTypography: QuackTypography = typography @Stable public fun buttonColors( @@ -253,88 +285,130 @@ public class QuackLargeButtonDefaults internal constructor() : disabledContentColor: QuackColor = QuackColor.White, borderColor: QuackColor = backgroundColor, disabledBorderColor: QuackColor = disabledBackgroundColor, + rippleColor: QuackColor = QuackColor.Unspecified, iconColor: QuackColor = contentColor, - ): QuackButtonColors { - return QuackButtonColors( + ): QuackButtonColors = + QuackButtonColors( backgroundColor = backgroundColor, disabledBackgroundColor = disabledBackgroundColor, contentColor = contentColor, disabledContentColor = disabledContentColor, borderColor = borderColor, disabledBorderColor = disabledBorderColor, - rippleColor = QuackColor.Unspecified, + rippleColor = rippleColor, iconColor = iconColor, ) - } - override fun invoke(styleBuilder: QuackLargeButtonDefaults.() -> Unit): QuackLargeButtonDefaults { - return apply(styleBuilder) - } + override fun invoke(styleBuilder: QuackPrimaryLargeButtonDefaults.() -> Unit): QuackPrimaryLargeButtonDefaults = + apply(styleBuilder) - override fun toString(): String = "QuackLargeButtonDefaults" + override fun toString(): String = this::class.simpleName!! } -/** - * 꽥꽥 디자인 가이드의 `Buttons#MediumButton`에 해당하는 디자인 스펙을 정의합니다. - * - * ### mutation 가능한 필드 - * - * - colors(iconColor) - */ -@Immutable -public class QuackMediumButtonDefaults internal constructor() : - QuackButtonStyle, ButtonStyleMarker { - - override var colors: QuackButtonColors = QuackButtonColors( - backgroundColor = QuackColor.White, - disabledBackgroundColor = QuackColor.White, - contentColor = QuackColor.Black, - disabledContentColor = QuackColor.DuckieOrange, - borderColor = QuackColor.DuckieOrange, - disabledBorderColor = QuackColor.Gray3, - rippleColor = QuackColor.Unspecified, - iconColor = QuackColor.Black, +/** 버튼 디자인 가이드의 `LargeButtons # secondary` 디자인 스펙을 정의합니다. */ +@Stable +public class QuackSecondaryLargeButtonDefaults internal constructor() : + QuackButtonStyle, QuackLargeButtonStyle { + + override var colors: QuackButtonColors = buttonColors() + + override var radius: Dp = 8.dp + + override var contentPadding: QuackPadding = QuackPadding( + horizontal = 112.dp, + vertical = 12.dp, ) + override var iconSpacedBy: Dp = 4.dp + + override var borderThickness: Dp = 1.dp + + override var typography: QuackTypography = QuackTypography.Subtitle + override var disabledTypography: QuackTypography = typography + + @Stable + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.White, + disabledBackgroundColor: QuackColor = backgroundColor, + contentColor: QuackColor = QuackColor.Black, + disabledContentColor: QuackColor = contentColor, + borderColor: QuackColor = QuackColor.Gray3, + disabledBorderColor: QuackColor = borderColor, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = contentColor, + ): QuackButtonColors = + QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) + + override fun invoke(styleBuilder: QuackSecondaryLargeButtonDefaults.() -> Unit): QuackSecondaryLargeButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! +} - override val radius: Dp = 8.dp +/** 버튼 디자인 가이드의 `MediumButton` 디자인 스펙을 정의합니다. */ +@Stable +public class QuackMediumButtonDefaults internal constructor() : + QuackButtonStyle, QuackMediumButtonStyle { + + override var colors: QuackButtonColors = buttonColors() - override val contentPadding: QuackPadding = QuackPadding( + override var radius: Dp = 8.dp + + override var contentPadding: QuackPadding = QuackPadding( horizontal = 58.dp, vertical = 10.dp, ) - override val iconSpacedBy: Dp = 4.dp + override var iconSpacedBy: Dp = 4.dp - override val borderThickness: Dp = 1.dp + override var borderThickness: Dp = 1.dp - override val typography: QuackTypography = QuackTypography.Body1 - override val disabledTypography: QuackTypography = QuackTypography.Title2 + override var typography: QuackTypography = QuackTypography.Body1 + override var disabledTypography: QuackTypography = QuackTypography.Title2 @Stable - public fun buttonColors(iconColor: QuackColor = colors.iconColor): QuackButtonColors { - return colors.copy(iconColor = iconColor) - } + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.White, + disabledBackgroundColor: QuackColor = QuackColor.Unspecified, + contentColor: QuackColor = QuackColor.Black, + disabledContentColor: QuackColor = QuackColor.Unspecified, + borderColor: QuackColor = QuackColor.DuckieOrange, + disabledBorderColor: QuackColor = QuackColor.Unspecified, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = contentColor, + ): QuackButtonColors = + QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) - override fun invoke(styleBuilder: QuackMediumButtonDefaults.() -> Unit): QuackMediumButtonDefaults { - return apply(styleBuilder) - } + override fun invoke(styleBuilder: QuackMediumButtonDefaults.() -> Unit): QuackMediumButtonDefaults = + apply(styleBuilder) - override fun toString(): String = "QuackMediumButtonDefaults" + override fun toString(): String = this::class.simpleName!! } /** - * 꽥꽥 디자인 가이드의 `Buttons#SmallButtons`에 해당하는 디자인 스펙을 정의합니다. + * 버튼 디자인 가이드의 `SmallButtons # primary, filled` 디자인 스펙을 정의합니다. * - * ### mutation 가능한 필드 - * - * - colors(backgroundColor, disabledBackgroundColor, contentColor, - * disabledContentColor, borderColor, disabledBorderColor) - * - radius - * - contentPadding - * - typography, disabledTypography + * `SmallButton`은 기본적으로 너비가 좁기에 데코레이터를 허용하지 않습니다. */ -@Immutable -public class QuackSmallButtonDefaults internal constructor() : - QuackButtonStyle, ButtonStyleMarker { +@Stable +public class QuackPrimaryFilledSmallButtonDefaults internal constructor() : + QuackButtonStyle, QuackSmallButtonStyle { override var colors: QuackButtonColors = buttonColors() @@ -344,12 +418,12 @@ public class QuackSmallButtonDefaults internal constructor() : horizontal = 12.dp, vertical = 8.dp, ) - override val iconSpacedBy: Dp = 4.dp + override var iconSpacedBy: Dp = Dp.Unspecified - override val borderThickness: Dp = 1.dp + override var borderThickness: Dp = 1.dp override var typography: QuackTypography = QuackTypography.Body1 - override var disabledTypography: QuackTypography = QuackTypography.Body1 + override var disabledTypography: QuackTypography = typography @Stable public fun buttonColors( @@ -359,6 +433,167 @@ public class QuackSmallButtonDefaults internal constructor() : disabledContentColor: QuackColor = QuackColor.White, borderColor: QuackColor = backgroundColor, disabledBorderColor: QuackColor = disabledBackgroundColor, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = QuackColor.Unspecified, + ): QuackButtonColors { + return QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) + } + + override fun invoke(styleBuilder: QuackPrimaryFilledSmallButtonDefaults.() -> Unit): QuackPrimaryFilledSmallButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! +} + +/** + * 버튼 디자인 가이드의 `SmallButtons # primary, outlined` 디자인 스펙을 정의합니다. + * + * `SmallButton`은 기본적으로 너비가 좁기에 데코레이터를 허용하지 않습니다. + */ +@Stable +public class QuackPrimaryOutlinedSmallButtonDefaults internal constructor() : + QuackButtonStyle, QuackSmallButtonStyle { + + override var colors: QuackButtonColors = buttonColors() + + override var radius: Dp = 8.dp + + override var contentPadding: QuackPadding = QuackPadding( + horizontal = 12.dp, + vertical = 8.dp, + ) + override var iconSpacedBy: Dp = Dp.Unspecified + + override var borderThickness: Dp = 1.dp + + override var typography: QuackTypography = QuackTypography.Body1 + override var disabledTypography: QuackTypography = typography + + @Stable + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.Gray3, + disabledBackgroundColor: QuackColor = QuackColor.White, + contentColor: QuackColor = QuackColor.Gray1, + disabledContentColor: QuackColor = QuackColor.DuckieOrange, + borderColor: QuackColor = backgroundColor, + disabledBorderColor: QuackColor = disabledContentColor, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = QuackColor.Unspecified, + ): QuackButtonColors { + return QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) + } + + override fun invoke(styleBuilder: QuackPrimaryOutlinedSmallButtonDefaults.() -> Unit): QuackPrimaryOutlinedSmallButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! +} + +/** + * 버튼 디자인 가이드의 `SmallButtons # primary, outlined, round` 디자인 스펙을 정의합니다. + * + * `SmallButton`은 기본적으로 너비가 좁기에 데코레이터를 허용하지 않습니다. + */ +@Stable +public class QuackPrimaryOutlinedRoundSmallButtonDefaults internal constructor() : + QuackButtonStyle, QuackSmallButtonStyle { + + override var colors: QuackButtonColors = buttonColors() + + override var radius: Dp = 30.dp + + override var contentPadding: QuackPadding = QuackPadding( + horizontal = 8.dp, + vertical = 4.dp, + ) + override var iconSpacedBy: Dp = Dp.Unspecified + + override var borderThickness: Dp = 1.dp + + override var typography: QuackTypography = QuackTypography.Subtitle2 + override var disabledTypography: QuackTypography = typography + + @Stable + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.Gray3, + disabledBackgroundColor: QuackColor = QuackColor.White, + contentColor: QuackColor = QuackColor.Gray1, + disabledContentColor: QuackColor = QuackColor.DuckieOrange, + borderColor: QuackColor = backgroundColor, + disabledBorderColor: QuackColor = disabledContentColor, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = QuackColor.Unspecified, + ): QuackButtonColors { + return QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) + } + + override fun invoke(styleBuilder: QuackPrimaryOutlinedRoundSmallButtonDefaults.() -> Unit): QuackPrimaryOutlinedRoundSmallButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! +} + +/** + * 버튼 디자인 가이드의 `SmallButtons # secondary` 디자인 스펙을 정의합니다. + * + * `SmallButton`은 기본적으로 너비가 좁기에 데코레이터를 허용하지 않습니다. + */ +@Stable +public class QuackSecondarySmallButtonDefaults internal constructor() : + QuackButtonStyle, QuackSmallButtonStyle { + + override var colors: QuackButtonColors = buttonColors() + + override var radius: Dp = 8.dp + + override var contentPadding: QuackPadding = QuackPadding( + horizontal = 12.dp, + vertical = 7.dp, + ) + override var iconSpacedBy: Dp = Dp.Unspecified + + override var borderThickness: Dp = 1.dp + + override var typography: QuackTypography = QuackTypography.Body1 + override var disabledTypography: QuackTypography = typography + + @Stable + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.White, + disabledBackgroundColor: QuackColor = QuackColor.Unspecified, + contentColor: QuackColor = QuackColor.Gray1, + disabledContentColor: QuackColor = QuackColor.Unspecified, + borderColor: QuackColor = QuackColor.Gray3, + disabledBorderColor: QuackColor = QuackColor.Unspecified, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = QuackColor.Unspecified, ): QuackButtonColors { return QuackButtonColors( backgroundColor = backgroundColor, @@ -367,16 +602,68 @@ public class QuackSmallButtonDefaults internal constructor() : disabledContentColor = disabledContentColor, borderColor = borderColor, disabledBorderColor = disabledBorderColor, - rippleColor = QuackColor.Unspecified, - iconColor = QuackColor.Unspecified, + rippleColor = rippleColor, + iconColor = iconColor, ) } - override fun invoke(styleBuilder: QuackSmallButtonDefaults.() -> Unit): QuackSmallButtonDefaults { - return apply(styleBuilder) + override fun invoke(styleBuilder: QuackSecondarySmallButtonDefaults.() -> Unit): QuackSecondarySmallButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! +} + +/** + * 버튼 디자인 가이드의 `SmallButtons # secondary, round` 디자인 스펙을 정의합니다. + * + * `SmallButton`은 기본적으로 너비가 좁기에 데코레이터를 허용하지 않습니다. + */ +@Stable +public class QuackSecondaryRoundSmallButtonDefaults internal constructor() : + QuackButtonStyle, QuackSmallButtonStyle { + + override var colors: QuackButtonColors = buttonColors() + + override var radius: Dp = 30.dp + + override var contentPadding: QuackPadding = QuackPadding( + horizontal = 8.dp, + vertical = 4.dp, + ) + override var iconSpacedBy: Dp = Dp.Unspecified + + override var borderThickness: Dp = 1.dp + + override var typography: QuackTypography = QuackTypography.Subtitle2 + override var disabledTypography: QuackTypography = typography + + @Stable + public fun buttonColors( + backgroundColor: QuackColor = QuackColor.White, + disabledBackgroundColor: QuackColor = QuackColor.Unspecified, + contentColor: QuackColor = QuackColor.Gray1, + disabledContentColor: QuackColor = QuackColor.Unspecified, + borderColor: QuackColor = QuackColor.Gray3, + disabledBorderColor: QuackColor = QuackColor.Unspecified, + rippleColor: QuackColor = QuackColor.Unspecified, + iconColor: QuackColor = QuackColor.Unspecified, + ): QuackButtonColors { + return QuackButtonColors( + backgroundColor = backgroundColor, + disabledBackgroundColor = disabledBackgroundColor, + contentColor = contentColor, + disabledContentColor = disabledContentColor, + borderColor = borderColor, + disabledBorderColor = disabledBorderColor, + rippleColor = rippleColor, + iconColor = iconColor, + ) } - override fun toString(): String = "QuackSmallButtonDefaults" + override fun invoke(styleBuilder: QuackSecondaryRoundSmallButtonDefaults.() -> Unit): QuackSecondaryRoundSmallButtonDefaults = + apply(styleBuilder) + + override fun toString(): String = this::class.simpleName!! } /** @@ -460,15 +747,14 @@ public fun Modifier.icons( * * ### 사용 가능 데코레이터 * - * | style | [icons][Modifier.icons] | description | - * | :---------------------------------: | :---------------------: | :----------------------------------------------------------: | - * | [Large][QuackLargeButtonDefaults] | ⭕ | | - * | [Medium][QuackMediumButtonDefaults] | ⭕ | | - * | [Small][QuackSmallButtonDefaults] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | + * | style | [icons][Modifier.icons] | description | + * | :-------------------------------: | :---------------------: | :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | | + * | [Medium][QuackMediumButtonStyle] | ⭕ | | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. | * * @param enabled 활성화 상태 여부 - * @param style 적용할 스타일. 사전 정의 스타일로 [QuackButtonStyle.Large][QuackLargeButtonDefaults], - * [QuackButtonStyle.Medium][QuackMediumButtonDefaults], [QuackButtonStyle.Small][QuackSmallButtonDefaults]이 제공됩니다. + * @param style 적용할 스타일. 사전 정의 스타일은 [QuackButtonStyle.Companion] 필드를 참고하세요. * @param text 중앙에 표시할 텍스트 * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. @@ -485,7 +771,7 @@ public fun QuackButton( rippleEnabled: Boolean = true, @CasaValue("{}") onClick: () -> Unit, ) { - val isSmallButton = style is QuackSmallButtonDefaults + val isSmallButton = style is QuackSmallButtonStyle // TODO: 다른 경우로 사이즈를 지정하는 방법이 있을까? // TODO(3): LayoutModifierNode 지원 var isSizeSpecified = false diff --git a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt b/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt index 1b14afca3..90c87ea42 100644 --- a/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt +++ b/ui/src/main/kotlin/team/duckie/quackquack/ui/sugar/button.kt @@ -79,18 +79,109 @@ import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi * * ### 사용 가능 데코레이터 * - * | style | [icons][Modifier.icons] | - * description | - * | :---------------------------------: | :---------------------: | - * :----------------------------------------------------------: | - * | [Large][QuackLargeButtonDefaults] | ⭕ | - * | - * | [Medium][QuackMediumButtonDefaults] | ⭕ | - * | - * | [Small][QuackSmallButtonDefaults] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 - * 없습니다. | - * - * This component uses [QuackButtonStyle.Large] as the token value for `style`. + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.PrimaryLarge] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +public fun QuackPrimaryLargeButton( + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + @CasaValue("\"QuackButton is experimental\"") text: String, + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +): Unit { + QuackButton( + modifier = modifier, + enabled = enabled, + style = QuackButtonStyle.PrimaryLarge, + text = text, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. + * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. + * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. + * + * ### 패딩 정책 + * + * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 + * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 + * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 + * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. + * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] + * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 + * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 + * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 + * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] + * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 + * [contentPadding][QuackButtonStyle.contentPadding]이 + * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 + * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. + * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 + * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 + * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) + * + * ### 배치 정책 + * + * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 + * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 + * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 + * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. + * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 + * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 + * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. + * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 + * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 + * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 + * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, + * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 + * 위해 이 정책이 사용됩니다. + * + * ### 사용 가능 데코레이터 + * + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.SecondaryLarge] as the token value for `style`. * * This document was automatically generated by [QuackButton]. * If any contents are broken, please check the original document. @@ -105,7 +196,7 @@ import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi @NonRestartableComposable @ExperimentalQuackQuackApi @SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackLargeButton( +public fun QuackSecondaryLargeButton( modifier: Modifier = sugar(), enabled: Boolean = sugar(), @CasaValue("\"QuackButton is experimental\"") text: String, @@ -115,7 +206,7 @@ public fun QuackLargeButton( QuackButton( modifier = modifier, enabled = enabled, - style = QuackButtonStyle.Large, + style = QuackButtonStyle.SecondaryLarge, text = text, rippleEnabled = rippleEnabled, onClick = onClick, @@ -170,16 +261,16 @@ public fun QuackLargeButton( * * ### 사용 가능 데코레이터 * - * | style | [icons][Modifier.icons] | - * description | - * | :---------------------------------: | :---------------------: | - * :----------------------------------------------------------: | - * | [Large][QuackLargeButtonDefaults] | ⭕ | - * | - * | [Medium][QuackMediumButtonDefaults] | ⭕ | - * | - * | [Small][QuackSmallButtonDefaults] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 - * 없습니다. | + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | * * This component uses [QuackButtonStyle.Medium] as the token value for `style`. * @@ -261,18 +352,382 @@ public fun QuackMediumButton( * * ### 사용 가능 데코레이터 * - * | style | [icons][Modifier.icons] | - * description | - * | :---------------------------------: | :---------------------: | - * :----------------------------------------------------------: | - * | [Large][QuackLargeButtonDefaults] | ⭕ | - * | - * | [Medium][QuackMediumButtonDefaults] | ⭕ | - * | - * | [Small][QuackSmallButtonDefaults] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 - * 없습니다. | + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.PrimaryFilledSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +public fun QuackPrimaryFilledSmallButton( + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + @CasaValue("\"QuackButton is experimental\"") text: String, + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +): Unit { + QuackButton( + modifier = modifier, + enabled = enabled, + style = QuackButtonStyle.PrimaryFilledSmall, + text = text, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. + * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. + * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. + * + * ### 패딩 정책 + * + * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 + * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 + * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 + * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. + * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] + * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 + * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 + * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 + * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] + * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 + * [contentPadding][QuackButtonStyle.contentPadding]이 + * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 + * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. + * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 + * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 + * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) + * + * ### 배치 정책 + * + * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 + * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 + * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 + * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. + * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 + * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 + * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. + * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 + * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 + * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 + * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, + * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 + * 위해 이 정책이 사용됩니다. + * + * ### 사용 가능 데코레이터 + * + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.PrimaryOutlinedSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +public fun QuackPrimaryOutlinedSmallButton( + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + @CasaValue("\"QuackButton is experimental\"") text: String, + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +): Unit { + QuackButton( + modifier = modifier, + enabled = enabled, + style = QuackButtonStyle.PrimaryOutlinedSmall, + text = text, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. + * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. + * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. + * + * ### 패딩 정책 + * + * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 + * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 + * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 + * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. + * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] + * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 + * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 + * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 + * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] + * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 + * [contentPadding][QuackButtonStyle.contentPadding]이 + * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 + * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. + * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 + * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 + * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) + * + * ### 배치 정책 + * + * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 + * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 + * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 + * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. + * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 + * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 + * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. + * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 + * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 + * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 + * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, + * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 + * 위해 이 정책이 사용됩니다. + * + * ### 사용 가능 데코레이터 + * + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.PrimaryOutlinedRoundSmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +public fun QuackPrimaryOutlinedRoundSmallButton( + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + @CasaValue("\"QuackButton is experimental\"") text: String, + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +): Unit { + QuackButton( + modifier = modifier, + enabled = enabled, + style = QuackButtonStyle.PrimaryOutlinedRoundSmall, + text = text, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. + * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. + * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. + * + * ### 패딩 정책 + * + * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 + * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 + * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 + * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. + * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] + * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 + * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 + * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 + * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] + * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 + * [contentPadding][QuackButtonStyle.contentPadding]이 + * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 + * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. + * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 + * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 + * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) + * + * ### 배치 정책 + * + * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 + * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 + * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 + * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. + * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 + * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 + * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. + * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 + * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 + * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 + * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, + * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 + * 위해 이 정책이 사용됩니다. + * + * ### 사용 가능 데코레이터 + * + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | + * + * This component uses [QuackButtonStyle.SecondarySmall] as the token value for `style`. + * + * This document was automatically generated by [QuackButton]. + * If any contents are broken, please check the original document. + * + * @param enabled 활성화 상태 여부 + * @param text 중앙에 표시할 텍스트 + * @param rippleEnabled 클릭했을 때 리플 애니메이션을 적용할지 여부 + * @param onClick 클릭했을 때 실행할 람다식. [enabled]이 true일 때만 작동합니다. + */ +@Casa +@Composable +@NonRestartableComposable +@ExperimentalQuackQuackApi +@SugarRefer("team.duckie.quackquack.ui.QuackButton") +public fun QuackSecondarySmallButton( + modifier: Modifier = sugar(), + enabled: Boolean = sugar(), + @CasaValue("\"QuackButton is experimental\"") text: String, + rippleEnabled: Boolean = sugar(), + @CasaValue("{}") onClick: () -> Unit, +): Unit { + QuackButton( + modifier = modifier, + enabled = enabled, + style = QuackButtonStyle.SecondarySmall, + text = text, + rippleEnabled = rippleEnabled, + onClick = onClick, + ) +} + +/** + * 버튼을 그립니다. + * + * - 이 컴포넌트는 자체의 패딩 정책을 구현합니다. + * - 이 컴포넌트는 자체의 배치 정책을 구현합니다. + * - [스타일][style]별로 사용 가능한 데코레이터가 달라집니다. + * + * ### 패딩 정책 + * + * 1. [버튼의 스타일][QuackButtonStyle]에서 [contentPadding][QuackButtonStyle.contentPadding] 옵션을 + * 별도로 제공하고 있습니다. 이는 [Modifier.padding]과 다른 패딩 정책을 사용합니다. [Modifier.padding]은 + * 버튼의 루트 레이아웃을 기준으로 패딩이 적용되지만, [QuackButtonStyle.contentPadding]은 버튼의 + * 텍스트를 기준으로 패딩이 적용됩니다. 이 부분의 자세한 내용은 배치 정책 세션을 참고하세요. + * 2. [LayoutModifier]를 사용하여 컴포넌트의 사이즈가 명시됐다면 [QuackButtonStyle.contentPadding] + * 옵션은 무시됩니다. [contentPadding][QuackButtonStyle.contentPadding]은 컴포넌트 사이즈 하드코딩을 + * 대체하는 용도로 제공됩니다. 하지만 컴포넌트 사이즈가 하드코딩됐다면 [contentPadding][QuackButtonStyle.contentPadding]을 + * 제공하는 의미가 없어집니다. 따라서 컴포넌트의 사이즈가 하드코딩됐다면 개발자의 의도를 존중한다는 원칙하에 + * 컴포넌트의 사이즈가 중첩으로 확장되는 일을 예방하고자 [contentPadding][QuackButtonStyle.contentPadding] + * 옵션을 무시합니다. 예를 들어 `Modifier.height(10.dp)`로 컴포넌트 높이를 명시했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(vertical=10.dp)`을 제공했다고 해봅시다. 이런 경우에는 + * [contentPadding][QuackButtonStyle.contentPadding]이 + * 무시되고 버튼의 높이가 10dp로 적용됩니다. 컴포넌트 사이즈를 명시하면서 패딩을 적용하고 싶다면 + * [contentPadding][QuackButtonStyle.contentPadding] 대신에 [Modifier.padding]을 사용하세요. + * [LayoutModifier]를 사용하는 흔한 [Modifier]로는 [Modifier.size], [Modifier.height], [Modifier.width] 등이 + * 있습니다. [LayoutModifierNode]를 사용하는 [Modifier]는 [contentPadding][QuackButtonStyle.contentPadding] 무시 + * 옵션이 아직 지원되지 않습니다. ([#636](https://github.com/duckie-team/quack-quack-android/issues/636)) + * + * ### 배치 정책 + * + * [style.contentPadding][QuackButtonStyle.contentPadding]은 항상 버튼의 텍스트를 기준으로 + * 적용됩니다. 예를 들어 버튼의 아이콘을 leading과 trailing을 모두 제공했고, + * [contentPadding][QuackButtonStyle.contentPadding]으로 + * `QuackPadding(horizontal=10.dp)`를 제공했다면 양끝의 horizontal 패딩이 각각 아이콘을 기준으로 + * 적용되는 게 아닌 버튼의 텍스트를 기준으로 적용됩니다. 따라서 개발자는 [contentPadding][QuackButtonStyle.contentPadding]의 값을 + * 제공할 때 양끝 아이콘을 기준으로 제공하는 게 아닌 가운데 텍스트를 기준으로 제공해야 합니다. + * 이 정책은 양끝 아이콘이 동적으로 적용될 때 의도하지 않는 버튼 사이즈 변경을 예방하기 위해 + * 고안됐습니다. 예를 들어 `contentPadding: QuackPadding(horizontal=10.dp)`을 양끝 아이콘 기준으로 + * 적용했다고 해봅시다. 처음에는 양끝에 아이콘이 없어서 가운데 텍스트를 기준으로 패딩이 적용됩니다. + * 이 시점에는 버튼의 너비가 25dp입니다. (왼쪽 패딩 10dp, 텍스트 5dp, 오른쪽 패딩 10dp) 사용자 + * 요청에 의해 양쪽 모두에 5dp의 너비를 갖는 아이콘이 추가되었습니다. 이 시점에서는 양쪽 아이콘이 + * 존재하므로 [contentPadding][QuackButtonStyle.contentPadding]이 양쪽 아이콘을 기준으로 적용되어 + * 버튼의 너비가 35dp입니다. (왼쪽 패딩 10dp, 왼쪽 아이콘 5dp, 텍스트 5dp, 오른쪽 아이콘 5dp, + * 오른쪽 패딩 10dp) 즉, 의도하지 않게 버튼의 너비가 10dp 증가하였습니다. 이러한 상황을 예방하기 + * 위해 이 정책이 사용됩니다. + * + * ### 사용 가능 데코레이터 + * + * | style | [icons][Modifier.icons] | description + * | + * | :-------------------------------: | :---------------------: | + * :-----------------------------------------------------: | + * | [Large][QuackLargeButtonStyle] | ⭕ | + * | + * | [Medium][QuackMediumButtonStyle] | ⭕ | + * | + * | [Small][QuackSmallButtonStyle] | ❌ | 버튼의 너비가 좁기에 아이콘 데코레이터를 사용할 수 없습니다. + * | * - * This component uses [QuackButtonStyle.Small] as the token value for `style`. + * This component uses [QuackButtonStyle.SecondaryRoundSmall] as the token value for `style`. * * This document was automatically generated by [QuackButton]. * If any contents are broken, please check the original document. @@ -287,7 +742,7 @@ public fun QuackMediumButton( @NonRestartableComposable @ExperimentalQuackQuackApi @SugarRefer("team.duckie.quackquack.ui.QuackButton") -public fun QuackSmallButton( +public fun QuackSecondaryRoundSmallButton( modifier: Modifier = sugar(), enabled: Boolean = sugar(), @CasaValue("\"QuackButton is experimental\"") text: String, @@ -297,7 +752,7 @@ public fun QuackSmallButton( QuackButton( modifier = modifier, enabled = enabled, - style = QuackButtonStyle.Small, + style = QuackButtonStyle.SecondaryRoundSmall, text = text, rippleEnabled = rippleEnabled, onClick = onClick, diff --git a/website/docs/releases.mdx b/website/docs/releases.mdx index d0a974185..117a42be3 100644 --- a/website/docs/releases.mdx +++ b/website/docs/releases.mdx @@ -17,5 +17,20 @@ sidebar_label: Releases - [runtime] Initial release. - [material] Initial release. - [animation] Initial release. -- [ui] Initial release with `QuackText`, `QuackButton`. +- [ui] Initial release with `QuackText`. - [util] Initial release. + +## 2.0.0-alpha02 + +*insert released date* + +*Target: BOM, runtime, material, animation, ui, util* + +### New + +- [BOM] Update the BOM delivery libraries version. +- [runtime] Improve `QuackComposedModifier` support. +- [material] Improved documentation. +- [animation] Improve documentation. +- [ui] Add a new `QuackButton` component. +- [util] Improve documentation.