Skip to content

Commit 2788ae6

Browse files
committed
feature: 동아리 탭에서 사용할 컴포넌트 구현
1 parent 621ae7b commit 2788ae6

File tree

10 files changed

+709
-0
lines changed

10 files changed

+709
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.ku_stacks.ku_ring.main.club.compose.components
2+
3+
import androidx.compose.foundation.BorderStroke
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.FlowRow
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.Spacer
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.shape.RoundedCornerShape
13+
import androidx.compose.material3.ExperimentalMaterial3Api
14+
import androidx.compose.material3.ModalBottomSheet
15+
import androidx.compose.material3.Surface
16+
import androidx.compose.material3.Text
17+
import androidx.compose.material3.rememberModalBottomSheetState
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.getValue
20+
import androidx.compose.runtime.mutableStateOf
21+
import androidx.compose.runtime.remember
22+
import androidx.compose.runtime.rememberCoroutineScope
23+
import androidx.compose.runtime.setValue
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.unit.dp
28+
import com.ku_stacks.ku_ring.designsystem.components.KuringCallToAction
29+
import com.ku_stacks.ku_ring.designsystem.components.LightAndDarkPreview
30+
import com.ku_stacks.ku_ring.designsystem.kuringtheme.KuringTheme
31+
import com.ku_stacks.ku_ring.designsystem.utils.ensureLineHeight
32+
import com.ku_stacks.ku_ring.main.club.compose.type.ClubDivisionChipItem
33+
import kotlinx.coroutines.launch
34+
35+
@OptIn(ExperimentalMaterial3Api::class)
36+
@Composable
37+
fun ClubDivisionBottomSheet(
38+
selectedItems: Set<ClubDivisionChipItem>,
39+
onConfirm: (Set<ClubDivisionChipItem>) -> Unit,
40+
onDismissRequest: () -> Unit,
41+
modifier: Modifier = Modifier,
42+
) {
43+
val sheetState = rememberModalBottomSheetState()
44+
val scope = rememberCoroutineScope()
45+
var currentSelected by remember { mutableStateOf(selectedItems) }
46+
47+
ModalBottomSheet(
48+
onDismissRequest = onDismissRequest,
49+
sheetState = sheetState,
50+
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
51+
dragHandle = null,
52+
containerColor = KuringTheme.colors.background,
53+
modifier = modifier,
54+
) {
55+
Column(
56+
modifier = Modifier.padding(
57+
start = 20.dp,
58+
end = 20.dp,
59+
top = 24.dp,
60+
),
61+
) {
62+
Text(
63+
text = "동아리 소속 선택",
64+
style = KuringTheme.typography.title2.ensureLineHeight(),
65+
color = KuringTheme.colors.textBody,
66+
)
67+
68+
Spacer(modifier = Modifier.height(16.dp))
69+
70+
ClubDivisionItemRow(
71+
selectedItems = currentSelected,
72+
onItemClick = { item ->
73+
currentSelected = if (currentSelected.contains(item)) {
74+
currentSelected - item
75+
} else {
76+
currentSelected + item
77+
}
78+
},
79+
)
80+
81+
Spacer(modifier = Modifier.height(16.dp))
82+
}
83+
84+
ActionButtonGroup(
85+
selectedItemCount = currentSelected.size,
86+
onConfirm = {
87+
scope.launch { sheetState.hide() }.invokeOnCompletion {
88+
if (!sheetState.isVisible) {
89+
onConfirm(currentSelected.toSet())
90+
}
91+
}
92+
},
93+
onReset = { currentSelected = emptySet() },
94+
modifier = Modifier.padding(
95+
start = 20.dp,
96+
end = 20.dp,
97+
bottom = 24.dp,
98+
),
99+
)
100+
}
101+
}
102+
103+
@Composable
104+
private fun ClubDivisionItemRow(
105+
selectedItems: Set<ClubDivisionChipItem>,
106+
onItemClick: (ClubDivisionChipItem) -> Unit,
107+
modifier: Modifier = Modifier,
108+
) {
109+
FlowRow(
110+
modifier = modifier,
111+
verticalArrangement = Arrangement.spacedBy(10.dp),
112+
horizontalArrangement = Arrangement.spacedBy(4.dp),
113+
) {
114+
ClubDivisionChipItem.entries.forEach { item ->
115+
ClubDivisionChipButton(
116+
item = item,
117+
isSelected = item in selectedItems,
118+
onClick = onItemClick,
119+
)
120+
}
121+
}
122+
}
123+
124+
@Composable
125+
private fun ActionButtonGroup(
126+
selectedItemCount: Int,
127+
onConfirm: () -> Unit,
128+
onReset: () -> Unit,
129+
modifier: Modifier = Modifier,
130+
) {
131+
Row(
132+
modifier = modifier,
133+
verticalAlignment = Alignment.CenterVertically,
134+
horizontalArrangement = Arrangement.spacedBy(12.dp),
135+
) {
136+
Surface(
137+
onClick = onReset,
138+
shape = RoundedCornerShape(100.dp),
139+
color = Color.Transparent,
140+
border = BorderStroke(1.dp, KuringTheme.colors.gray200),
141+
modifier = Modifier.height(56.dp),
142+
) {
143+
Box(
144+
contentAlignment = Alignment.Center,
145+
modifier = Modifier.padding(horizontal = 32.dp),
146+
) {
147+
Text(
148+
text = "초기화",
149+
style = KuringTheme.typography.body2SB,
150+
color = KuringTheme.colors.textCaption1,
151+
)
152+
}
153+
}
154+
155+
KuringCallToAction(
156+
text = "확인 (${selectedItemCount})",
157+
onClick = onConfirm,
158+
enabled = selectedItemCount > 0,
159+
modifier = Modifier.weight(1f),
160+
)
161+
}
162+
}
163+
164+
@LightAndDarkPreview
165+
@Composable
166+
private fun ClubDivisionBottomSheetPreview() {
167+
KuringTheme {
168+
ClubDivisionBottomSheet(
169+
selectedItems = setOf(),
170+
onConfirm = {},
171+
onDismissRequest = {},
172+
)
173+
}
174+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.ku_stacks.ku_ring.main.club.compose.components
2+
3+
import androidx.compose.animation.animateColor
4+
import androidx.compose.animation.core.updateTransition
5+
import androidx.compose.foundation.BorderStroke
6+
import androidx.compose.foundation.interaction.MutableInteractionSource
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.selection.toggleable
9+
import androidx.compose.foundation.shape.RoundedCornerShape
10+
import androidx.compose.material3.Surface
11+
import androidx.compose.material3.Text
12+
import androidx.compose.material3.ripple
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.getValue
15+
import androidx.compose.runtime.mutableStateOf
16+
import androidx.compose.runtime.remember
17+
import androidx.compose.runtime.setValue
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.draw.clip
20+
import androidx.compose.ui.unit.dp
21+
import com.ku_stacks.ku_ring.designsystem.components.LightAndDarkPreview
22+
import com.ku_stacks.ku_ring.designsystem.kuringtheme.KuringTheme
23+
import com.ku_stacks.ku_ring.designsystem.utils.ensureLineHeight
24+
import com.ku_stacks.ku_ring.main.club.compose.type.ClubDivisionChipItem
25+
26+
/**
27+
* 동아리 소속을 나타내고, 선택할 수 있는 버튼이다.
28+
*
29+
* @param item 동아리 소속
30+
* @param isSelected 선택 여부
31+
* @param modifier 동아리 소속 버튼에 적용될 [Modifier]
32+
* @param onClick 동아리 소속 버튼을 클릭했을 때 실행할 콜백
33+
*/
34+
@Composable
35+
internal fun ClubDivisionChipButton(
36+
item: ClubDivisionChipItem,
37+
isSelected: Boolean,
38+
modifier: Modifier = Modifier,
39+
onClick: (ClubDivisionChipItem) -> Unit
40+
) {
41+
val interactionSource = remember { MutableInteractionSource() }
42+
43+
val transition = updateTransition(targetState = isSelected, label = "ChipSelectionTransition")
44+
val containerColor by transition.animateColor(label = "containerColor") { selected ->
45+
if (selected) KuringTheme.colors.mainPrimarySelected else KuringTheme.colors.background
46+
}
47+
val contentColor by transition.animateColor(label = "contentColor") { selected ->
48+
if (selected) KuringTheme.colors.mainPrimary else KuringTheme.colors.textCaption1
49+
}
50+
val borderColor by transition.animateColor(label = "borderColor") { selected ->
51+
if (selected) KuringTheme.colors.mainPrimary else KuringTheme.colors.gray200
52+
}
53+
val shape = RoundedCornerShape(24.dp)
54+
55+
Surface(
56+
color = containerColor,
57+
contentColor = contentColor,
58+
shape = shape,
59+
border = BorderStroke(width = 1.dp, color = borderColor),
60+
modifier = modifier
61+
.clip(shape)
62+
.toggleable(
63+
value = isSelected,
64+
interactionSource = interactionSource,
65+
indication = ripple(),
66+
onValueChange = { onClick(item) },
67+
),
68+
) {
69+
Text(
70+
text = item.koreanName,
71+
style = KuringTheme.typography.caption1_1.ensureLineHeight(),
72+
modifier = Modifier
73+
.padding(
74+
horizontal = 14.dp,
75+
vertical = 8.dp,
76+
),
77+
)
78+
}
79+
}
80+
81+
@LightAndDarkPreview
82+
@Composable
83+
private fun ClubDivisionChipButtonPreview() {
84+
KuringTheme {
85+
var isSelected by remember { mutableStateOf(false) }
86+
87+
ClubDivisionChipButton(
88+
item = ClubDivisionChipItem.EXHIBITION_ARTS,
89+
isSelected = isSelected,
90+
onClick = { isSelected = !isSelected },
91+
)
92+
}
93+
}

0 commit comments

Comments
 (0)