Skip to content

Commit 9f3b38e

Browse files
authored
[Feat/#10] 버튼 공통 컴포넌트 구현 (#15)
* [Feat/#10] PotiActionButton 구현 * [Feat/#10] PotiChipButton 구현 * [Feat/#10] PotiFloatingButton 구현 * [Feat/#10] PotiIconButton 구현 * [Feat/#10] PotiInlineButton 구현 * [Feat/#10] PotiModalButton 구현 * [Feat/#10] PotiSmallButton 구현 * [Feat/#10] PotiTextButton 구현 * [Feat/#10] PotiModalButton 프리뷰 코드 정리 * [Feat/#10] PotiIconButton tint 파라미터 추가 * [Refactor/#10] 버튼 애니메이션 Transition으로 연결 * [Refactor/#10] 텍스트, 아이콘 버튼 Box 제거 * [Refactor/#10] enum class 통합 및 색상 로직 분리 * [Refactor/#10] height를 heightIn으로 변경 * [Refactor/#10] noRippleClickable 확장함수 분리 및 적용 * [Refactor/#10] 플로팅 버튼 애니메이션 size를 scale로 변경 * [Refactor/#10] 아이콘 painter를 imageVector로 변경 * [Chore/#10] Compose UI graphics 라이브러리 의존성 추가 * [Refactor/#10] dropShadow 확장함수 정의 및 적용 * [Refactor/#10] RoundedCornerShape을 CircleShape로 변경 * [Chore/#10] klint 적용 * [Feat/#10] PotiDeleteButton 구현 * [Refactor/#10] DeleteButtonType 네이밍 수정 * [Refactor/#10] PotiDeleteButton padding 값 수정 * [Chore/#10] Compose UI graphics 라이브러리 의존성 제거
1 parent 2aa20d5 commit 9f3b38e

File tree

12 files changed

+866
-2
lines changed

12 files changed

+866
-2
lines changed

app/src/main/java/com/poti/android/core/common/extension/ModifierExt.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
package com.poti.android.core.common.extension
22

3+
import android.graphics.BlurMaskFilter
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.border
56
import androidx.compose.foundation.clickable
67
import androidx.compose.foundation.interaction.MutableInteractionSource
78
import androidx.compose.foundation.shape.RoundedCornerShape
9+
import androidx.compose.runtime.Composable
810
import androidx.compose.runtime.remember
911
import androidx.compose.ui.Modifier
1012
import androidx.compose.ui.composed
1113
import androidx.compose.ui.draw.clip
14+
import androidx.compose.ui.draw.drawBehind
15+
import androidx.compose.ui.geometry.Size
1216
import androidx.compose.ui.graphics.Color
17+
import androidx.compose.ui.graphics.Paint
18+
import androidx.compose.ui.graphics.Shape
19+
import androidx.compose.ui.graphics.drawOutline
20+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
21+
import androidx.compose.ui.platform.LocalDensity
1322
import androidx.compose.ui.unit.Dp
1423
import androidx.compose.ui.unit.dp
1524

@@ -24,6 +33,17 @@ inline fun Modifier.noRippleClickable(
2433
}
2534
}
2635

36+
fun Modifier.noRippleClickable(
37+
interactionSource: MutableInteractionSource,
38+
enabled: Boolean = true,
39+
onClick: () -> Unit,
40+
): Modifier = this.clickable(
41+
interactionSource = interactionSource,
42+
indication = null,
43+
enabled = enabled,
44+
onClick = onClick,
45+
)
46+
2747
fun Modifier.roundedBackgroundWithBorder(
2848
cornerRadius: Dp,
2949
backgroundColor: Color,
@@ -39,3 +59,47 @@ fun Modifier.roundedBackgroundWithBorder(
3959
shape = RoundedCornerShape(cornerRadius),
4060
)
4161
}
62+
63+
@Composable
64+
fun Modifier.dropShadow(
65+
shape: Shape,
66+
color: Color = Color.Black.copy(0.25f),
67+
offsetX: Dp = 1.dp,
68+
offsetY: Dp = 1.dp,
69+
blur: Dp = 1.dp,
70+
spread: Dp = 1.dp,
71+
) = composed {
72+
val density = LocalDensity.current
73+
74+
val paint = remember(color, blur) {
75+
Paint().apply {
76+
this.color = color
77+
val blurPx = with(density) { blur.toPx() }
78+
if (blurPx > 0f) {
79+
this.asFrameworkPaint().maskFilter =
80+
BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL)
81+
}
82+
}
83+
}
84+
85+
drawBehind {
86+
val spreadPx = spread.toPx()
87+
val offsetXPx = offsetX.toPx()
88+
val offsetYPx = offsetY.toPx()
89+
90+
val shadowWidth = size.width + spreadPx
91+
val shadowHeight = size.height + spreadPx
92+
93+
if (shadowWidth <= 0f || shadowHeight <= 0f) return@drawBehind
94+
95+
val shadowSize = Size(shadowWidth, shadowHeight)
96+
val shadowOutline = shape.createOutline(shadowSize, layoutDirection, this)
97+
98+
drawIntoCanvas { canvas ->
99+
canvas.save()
100+
canvas.translate(offsetXPx, offsetYPx)
101+
canvas.drawOutline(shadowOutline, paint)
102+
canvas.restore()
103+
}
104+
}
105+
}

app/src/main/java/com/poti/android/core/designsystem/component/button/DummyButton.kt

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.poti.android.core.designsystem.component.button
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.interaction.MutableInteractionSource
5+
import androidx.compose.foundation.interaction.collectIsPressedAsState
6+
import androidx.compose.foundation.layout.Arrangement
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.heightIn
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.foundation.shape.CircleShape
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.*
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.draw.clip
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.tooling.preview.Preview
20+
import androidx.compose.ui.unit.dp
21+
import com.poti.android.core.common.extension.noRippleClickable
22+
import com.poti.android.core.designsystem.theme.PotiColors
23+
import com.poti.android.core.designsystem.theme.PotiTheme
24+
25+
@Composable
26+
fun PotiActionButton(
27+
text: String,
28+
onClick: () -> Unit,
29+
modifier: Modifier = Modifier,
30+
type: ActionButtonType = ActionButtonType.PRIMARY_MAIN,
31+
enabled: Boolean = true,
32+
) {
33+
val interactionSource = remember { MutableInteractionSource() }
34+
val isPressed by interactionSource.collectIsPressedAsState()
35+
val colors = PotiTheme.colors
36+
val backgroundColor = type.getBackgroundColor(colors, isPressed)
37+
val contentColor = type.getContentColor(colors)
38+
val isButtonEnabled = enabled && type != ActionButtonType.DEACTIVE_MAIN && type != ActionButtonType.DEACTIVE_SUB
39+
40+
Row(
41+
modifier = modifier
42+
.heightIn(min = 52.dp)
43+
.clip(CircleShape)
44+
.background(backgroundColor)
45+
.noRippleClickable(
46+
interactionSource = interactionSource,
47+
enabled = isButtonEnabled,
48+
onClick = onClick,
49+
)
50+
.padding(horizontal = 16.dp, vertical = 14.dp),
51+
horizontalArrangement = Arrangement.Center,
52+
verticalAlignment = Alignment.CenterVertically,
53+
) {
54+
Text(
55+
text = text,
56+
color = contentColor,
57+
style = PotiTheme.typography.button16sb,
58+
)
59+
}
60+
}
61+
62+
enum class ActionButtonType {
63+
PRIMARY_MAIN,
64+
PRIMARY_SUB,
65+
SECONDARY_MAIN,
66+
SECONDARY_SUB,
67+
DEACTIVE_MAIN,
68+
DEACTIVE_SUB,
69+
;
70+
71+
fun getBackgroundColor(
72+
colors: PotiColors,
73+
isPressed: Boolean,
74+
): Color =
75+
when (this) {
76+
PRIMARY_MAIN -> if (isPressed) colors.poti800 else colors.poti600
77+
PRIMARY_SUB -> if (isPressed) colors.gray300 else colors.gray100
78+
SECONDARY_MAIN -> if (isPressed) colors.gray900 else colors.black
79+
SECONDARY_SUB -> if (isPressed) colors.gray300 else colors.gray100
80+
DEACTIVE_MAIN -> colors.gray700
81+
DEACTIVE_SUB -> colors.gray100
82+
}
83+
84+
fun getContentColor(colors: PotiColors): Color {
85+
return when (this) {
86+
PRIMARY_MAIN, SECONDARY_MAIN, DEACTIVE_MAIN -> colors.white
87+
PRIMARY_SUB -> colors.poti600
88+
SECONDARY_SUB -> colors.gray900
89+
DEACTIVE_SUB -> colors.gray700
90+
}
91+
}
92+
}
93+
94+
@Preview(showBackground = true)
95+
@Composable
96+
private fun PotiActionButtonPreview() {
97+
PotiTheme {
98+
Column(
99+
modifier = Modifier.padding(16.dp),
100+
verticalArrangement = Arrangement.spacedBy(12.dp),
101+
) {
102+
PotiActionButton(
103+
text = "Primary Main",
104+
onClick = {},
105+
modifier = Modifier.width(328.dp),
106+
type = ActionButtonType.PRIMARY_MAIN,
107+
)
108+
109+
PotiActionButton(
110+
text = "Primary Sub",
111+
onClick = {},
112+
modifier = Modifier.width(328.dp),
113+
type = ActionButtonType.PRIMARY_SUB,
114+
)
115+
116+
PotiActionButton(
117+
text = "Secondary Main",
118+
onClick = {},
119+
modifier = Modifier.width(328.dp),
120+
type = ActionButtonType.SECONDARY_MAIN,
121+
)
122+
123+
PotiActionButton(
124+
text = "Secondary Sub",
125+
onClick = {},
126+
modifier = Modifier.width(328.dp),
127+
type = ActionButtonType.SECONDARY_SUB,
128+
)
129+
130+
PotiActionButton(
131+
text = "Deactive Main",
132+
onClick = {},
133+
modifier = Modifier.width(328.dp),
134+
type = ActionButtonType.DEACTIVE_MAIN,
135+
)
136+
137+
PotiActionButton(
138+
text = "Deactive Sub",
139+
onClick = {},
140+
modifier = Modifier.width(328.dp),
141+
type = ActionButtonType.DEACTIVE_SUB,
142+
)
143+
}
144+
}
145+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.poti.android.core.designsystem.component.button
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.interaction.MutableInteractionSource
5+
import androidx.compose.foundation.interaction.collectIsPressedAsState
6+
import androidx.compose.foundation.layout.Arrangement
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.heightIn
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.foundation.shape.RoundedCornerShape
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.*
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.draw.clip
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.tooling.preview.Preview
20+
import androidx.compose.ui.unit.dp
21+
import com.poti.android.core.common.extension.noRippleClickable
22+
import com.poti.android.core.designsystem.theme.PotiColors
23+
import com.poti.android.core.designsystem.theme.PotiTheme
24+
25+
@Composable
26+
fun PotiChipButton(
27+
text: String,
28+
onClick: () -> Unit,
29+
modifier: Modifier = Modifier,
30+
type: ChipButtonType = ChipButtonType.DEFAULT,
31+
) {
32+
val interactionSource = remember { MutableInteractionSource() }
33+
val isPressed by interactionSource.collectIsPressedAsState()
34+
val colors = PotiTheme.colors
35+
val backgroundColor = type.getBackgroundColor(colors, isPressed)
36+
val contentColor = type.getContentColor(colors)
37+
38+
Row(
39+
modifier = modifier
40+
.heightIn(min = 56.dp)
41+
.clip(RoundedCornerShape(12.dp))
42+
.background(backgroundColor)
43+
.noRippleClickable(
44+
interactionSource = interactionSource,
45+
onClick = onClick,
46+
)
47+
.padding(vertical = 16.dp),
48+
horizontalArrangement = Arrangement.Center,
49+
verticalAlignment = Alignment.CenterVertically,
50+
) {
51+
Text(
52+
text = text,
53+
color = contentColor,
54+
style = PotiTheme.typography.button16sb,
55+
)
56+
}
57+
}
58+
59+
enum class ChipButtonType {
60+
DEFAULT,
61+
SELECTED,
62+
;
63+
64+
fun getBackgroundColor(
65+
colors: PotiColors,
66+
isPressed: Boolean,
67+
): Color =
68+
when (this) {
69+
DEFAULT -> if (isPressed) colors.gray300 else colors.gray100
70+
SELECTED -> if (isPressed) colors.poti800 else colors.poti600
71+
}
72+
73+
fun getContentColor(colors: PotiColors): Color {
74+
return when (this) {
75+
DEFAULT -> colors.gray800
76+
SELECTED -> colors.white
77+
}
78+
}
79+
}
80+
81+
@Preview(showBackground = true)
82+
@Composable
83+
private fun PotiChipButtonPreview() {
84+
PotiTheme {
85+
Column(
86+
modifier = Modifier.padding(16.dp),
87+
verticalArrangement = Arrangement.spacedBy(12.dp),
88+
) {
89+
PotiChipButton(
90+
text = "Default",
91+
onClick = {},
92+
modifier = Modifier.width(165.dp),
93+
type = ChipButtonType.DEFAULT,
94+
)
95+
96+
PotiChipButton(
97+
text = "Selected",
98+
onClick = {},
99+
modifier = Modifier.width(165.dp),
100+
type = ChipButtonType.SELECTED,
101+
)
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)