-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat/#12] 공통 컴포넌트 button 구현 #13
Changes from 15 commits
1b586e0
5c90d13
95d0698
8d7062d
189c67e
0fccd08
1ee3a6f
495bb03
d7c1205
e26e8fc
8a1d854
51b52cd
2d95f95
5f59593
011597b
4fa3c3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
package com.spoony.spoony.core.designsystem.component.button | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.foundation.interaction.collectIsPressedAsState | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.PaddingValues | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.tooling.preview.PreviewParameter | ||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider | ||
import androidx.compose.ui.unit.dp | ||
import com.spoony.spoony.R | ||
import com.spoony.spoony.core.designsystem.theme.SpoonyAndroidTheme | ||
import com.spoony.spoony.core.designsystem.type.ButtonSize | ||
import com.spoony.spoony.core.designsystem.type.ButtonStyle | ||
|
||
@Composable | ||
fun SpoonyButton( | ||
text: String, | ||
size: ButtonSize, | ||
style: ButtonStyle, | ||
onClick: () -> Unit, | ||
modifier: Modifier = Modifier, | ||
enabled: Boolean = true, | ||
icon: (@Composable () -> Unit)? = null, | ||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } | ||
) { | ||
val isPressed by interactionSource.collectIsPressedAsState() | ||
val spoonyColors = SpoonyAndroidTheme.colors | ||
val spoonyTypography = SpoonyAndroidTheme.typography | ||
val backgroundColor = remember(enabled, isPressed, style) { | ||
when { | ||
!enabled -> when (style) { | ||
ButtonStyle.Primary -> spoonyColors.main100 | ||
ButtonStyle.Secondary -> spoonyColors.gray300 | ||
ButtonStyle.Tertiary -> spoonyColors.gray100 | ||
} | ||
isPressed -> when (style) { | ||
ButtonStyle.Primary -> spoonyColors.main500 | ||
ButtonStyle.Secondary -> spoonyColors.gray800 | ||
ButtonStyle.Tertiary -> spoonyColors.gray100 | ||
} | ||
else -> when (style) { | ||
ButtonStyle.Primary -> spoonyColors.main400 | ||
ButtonStyle.Secondary -> spoonyColors.black | ||
ButtonStyle.Tertiary -> spoonyColors.gray0 | ||
} | ||
} | ||
} | ||
val paddingValues = remember(size) { | ||
when (size) { | ||
ButtonSize.Xlarge -> PaddingValues(horizontal = 16.dp, vertical = 16.dp) | ||
ButtonSize.Large, ButtonSize.Medium, ButtonSize.Small -> PaddingValues(horizontal = 16.dp, vertical = 18.dp) | ||
ButtonSize.Xsmall -> PaddingValues(horizontal = 16.dp, vertical = 12.dp) | ||
} | ||
} | ||
|
||
val textStyle = remember(size) { | ||
when (size) { | ||
ButtonSize.Xlarge -> spoonyTypography.body1b | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: 폰트도!! 수정사항 피그마에 반영되었으니 확인 부탁드려요~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정했습니다! |
||
ButtonSize.Large, ButtonSize.Medium, ButtonSize.Small, ButtonSize.Xsmall -> spoonyTypography.body2b | ||
} | ||
} | ||
|
||
val textColor = remember(style) { | ||
when (style) { | ||
ButtonStyle.Tertiary -> when { | ||
!enabled -> spoonyColors.gray400 | ||
else -> spoonyColors.gray600 | ||
} | ||
else -> spoonyColors.white | ||
} | ||
} | ||
|
||
val cornerRadius = remember(size) { | ||
when (size) { | ||
ButtonSize.Xlarge -> 8.dp | ||
ButtonSize.Large, ButtonSize.Medium, ButtonSize.Small, ButtonSize.Xsmall -> 10.dp | ||
} | ||
} | ||
Row( | ||
modifier = modifier | ||
.clip(RoundedCornerShape(cornerRadius)) | ||
.background(color = backgroundColor) | ||
.clickable( | ||
enabled = enabled, | ||
indication = null, | ||
interactionSource = interactionSource, | ||
onClick = onClick | ||
) | ||
.padding(paddingValues), | ||
verticalAlignment = Alignment.CenterVertically, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p4: .semantics {
contentDescription = "..."
} 이런 접근성 챙겨주는것도 좋아보여요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오,, 처음 봤습니다! Row도 contentDescription 를 추가할 수 있었군요! 아직 어떤식으로 작성해야될지 모르겠어서 추후에 추가하도록 하겠습니다! |
||
horizontalArrangement = Arrangement.Center | ||
) { | ||
if (icon != null) { | ||
icon() | ||
Spacer(modifier = Modifier.width(8.dp)) | ||
} | ||
Text( | ||
text = text, | ||
color = textColor, | ||
style = textStyle | ||
) | ||
} | ||
} | ||
|
||
private class ButtonStyleProvider : PreviewParameterProvider<ButtonStyle> { | ||
override val values: Sequence<ButtonStyle> = sequenceOf( | ||
ButtonStyle.Primary, | ||
ButtonStyle.Secondary, | ||
ButtonStyle.Tertiary | ||
) | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun SpoonyButtonEnabledPreview( | ||
@PreviewParameter(ButtonStyleProvider::class) style: ButtonStyle | ||
) { | ||
SpoonyAndroidTheme { | ||
Column( | ||
modifier = Modifier, | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
ButtonSize.entries.forEach { size -> | ||
SpoonyButton( | ||
text = "버튼", | ||
style = style, | ||
size = size, | ||
onClick = { } | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun SpoonyButtonEnabledIconPreview( | ||
@PreviewParameter(ButtonStyleProvider::class) style: ButtonStyle | ||
) { | ||
SpoonyAndroidTheme { | ||
Column( | ||
modifier = Modifier, | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
ButtonSize.entries.forEach { size -> | ||
SpoonyButton( | ||
text = "버튼", | ||
style = style, | ||
size = size, | ||
onClick = { }, | ||
icon = { | ||
Icon( | ||
painter = painterResource(R.drawable.ic_launcher_foreground), | ||
modifier = Modifier.size(32.dp), | ||
contentDescription = "ic_spoon_button", | ||
tint = Color.Unspecified | ||
) | ||
} | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun SpoonyButtonDisabledPreview( | ||
@PreviewParameter(ButtonStyleProvider::class) style: ButtonStyle | ||
) { | ||
SpoonyAndroidTheme { | ||
Column( | ||
modifier = Modifier, | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
ButtonSize.entries.forEach { size -> | ||
SpoonyButton( | ||
text = "버튼", | ||
style = style, | ||
size = size, | ||
enabled = false, | ||
onClick = { } | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
private fun SpoonyButtonWidthModifierPreview() { | ||
SpoonyAndroidTheme { | ||
SpoonyButton( | ||
text = "떠먹으러 가기", | ||
onClick = {}, | ||
style = ButtonStyle.Secondary, | ||
size = ButtonSize.Xsmall, | ||
modifier = Modifier.width(134.dp) | ||
) | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
private fun SpoonyButtonTwoButtonPreview() { | ||
SpoonyAndroidTheme { | ||
Column( | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
Row( | ||
modifier = Modifier.fillMaxWidth(), | ||
horizontalArrangement = Arrangement.SpaceBetween | ||
) { | ||
SpoonyButton( | ||
text = "버튼", | ||
onClick = {}, | ||
style = ButtonStyle.Tertiary, | ||
size = ButtonSize.Xsmall, | ||
modifier = Modifier.weight(1f) | ||
) | ||
Spacer(modifier = Modifier.width(13.dp)) | ||
SpoonyButton( | ||
text = "버튼", | ||
onClick = {}, | ||
style = ButtonStyle.Secondary, | ||
size = ButtonSize.Xsmall, | ||
modifier = Modifier.weight(1f) | ||
) | ||
} | ||
Row( | ||
modifier = Modifier.fillMaxWidth(), | ||
horizontalArrangement = Arrangement.SpaceBetween | ||
) { | ||
SpoonyButton( | ||
text = "버튼", | ||
onClick = {}, | ||
style = ButtonStyle.Tertiary, | ||
size = ButtonSize.Xsmall, | ||
enabled = false, | ||
modifier = Modifier.weight(1f) | ||
) | ||
Spacer(modifier = Modifier.width(13.dp)) | ||
SpoonyButton( | ||
text = "버튼", | ||
onClick = {}, | ||
style = ButtonStyle.Secondary, | ||
size = ButtonSize.Xsmall, | ||
enabled = false, | ||
modifier = Modifier.weight(1f) | ||
) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 따봉 감사합니다! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.spoony.spoony.core.designsystem.type | ||
|
||
enum class ButtonSize { | ||
Xlarge, Large, Medium, Small, Xsmall | ||
} | ||
|
||
enum class ButtonStyle { | ||
Primary, Secondary, Tertiary | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,17 @@ | ||||||
package com.spoony.spoony.core.util.extension | ||||||
|
||||||
import androidx.compose.foundation.clickable | ||||||
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.composed | ||||||
|
||||||
@Composable | ||||||
inline fun Modifier.noRippleClickable(crossinline onClick: () -> Unit): Modifier = composed { | ||||||
this.clickable( | ||||||
indication = null, | ||||||
interactionSource = remember { MutableInteractionSource() }, | ||||||
onClick = { onClick() } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p3) 이 부분 이렇게도 되지 않나요? 불필요한 람다를 추가로 만들필요는 없을 것 같습니다.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 람다 전달과 호출의 차이람다 전달: onClick = onClick 여기서 onClick은 람다를 전달만 하고 호출은 나중에 이뤄집니다. 람다 호출: onClick = { onClick() } 전달받은 onClick 람다를 호출하도록 명시적으로 정의합니다. 왜 inline과 crossinline을 사용했는가?inline: 함수 호출 오버헤드를 줄이고, noRippleClickable 함수가 자주 호출될 때 성능을 최적화. 결론전달받은 람다를 명시적으로 호출해야 하기 때문에 onClick 을 사용하지 않고 { onClick() } 로 해야된다고 합니다. 일단 잘몰라서 정리해봤는데.. 아직도 사실 이해가 잘 가진 않습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이부분은 저도 잘 모르던 부분인데 찾아보고 공유하도록 하겠습니다! |
||||||
) | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: 이거 디쟌 수정되었으니 피그마 확인 부탁드립니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수정했습니다!