Skip to content
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

SnackBar 구현 #14

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
298f689
feat : make snackbar file
leeeyubin Sep 19, 2024
b5eb37c
feat : make snackbar preview file
leeeyubin Sep 19, 2024
08b3dc3
feat : make InfoSnackBar
leeeyubin Sep 19, 2024
435824b
feat : make ErrorSnackBar
leeeyubin Sep 19, 2024
47e4c76
feat : make AutoInfoSnackBar
leeeyubin Sep 20, 2024
a8337d7
feat : make AutoErrorSnackBar
leeeyubin Sep 22, 2024
aa92578
feat : change snackbar function name
leeeyubin Sep 22, 2024
d1e51cd
chore : 코드 수정
leeeyubin Sep 22, 2024
dc8b143
feat : 정보성 스낵바 주석 추가
leeeyubin Sep 22, 2024
ccd35a8
feat : 에러 스낵바 주석 추가
leeeyubin Sep 23, 2024
a7c3700
feat : 에러 스낵바 아이콘 정렬
leeeyubin Sep 23, 2024
e65c063
feat : 스낵바 지속시간 상수화
leeeyubin Sep 23, 2024
8f96b18
fix : 핸디 스낵바 컬러 추가
leeeyubin Sep 27, 2024
3955b5d
fix : modify to Handy Icon
leeeyubin Oct 3, 2024
7ab14c5
refactor : refactor snackBar code
leeeyubin Oct 3, 2024
e81f277
feat : git pull from main
leeeyubin Oct 30, 2024
90f60c9
feat : add error SnackBar type
leeeyubin Oct 30, 2024
b342e26
feat : currentSnackBarData 변수 사용
leeeyubin Oct 30, 2024
3b5b996
feat : 스와이프 기능 구현
leeeyubin Oct 31, 2024
5334b9a
feat : 주석 추가
leeeyubin Nov 1, 2024
b26f978
feat : 에러 스낵바 UI 수정 및 LineBreak 속성 추가
leeeyubin Nov 21, 2024
ac45fb4
feat : 정보성 스낵바 단어 개행 구현
leeeyubin Nov 22, 2024
c55e525
feat : 정보성 스낵바 단어 개행 구현
leeeyubin Nov 22, 2024
fc05bf7
feat : Text lineBreak 속성 제거
leeeyubin Nov 22, 2024
6af901d
feat : 에러 스낵바 단어 개행 구현
leeeyubin Nov 22, 2024
e2c7cb5
feat : 불필요한 Column 삭제
leeeyubin Nov 22, 2024
1b197c4
feat : git pull from main
leeeyubin Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Empty file.
53 changes: 53 additions & 0 deletions app/src/main/kotlin/com/yourssu/handy/demo/SnackBarPreview.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.yourssu.handy.demo

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.yourssu.handy.compose.ErrorSnackBar
import com.yourssu.handy.compose.HandyTheme
import com.yourssu.handy.compose.InfoSnackBar

@Preview(showBackground = true)
@Composable
fun InfoSnackBarPreview() {
HandyTheme {
Column(
modifier = Modifier.padding(10.dp)
) {
InfoSnackBar(
text = "한 줄짜리 정보성 메세지가 들어갑니다.",
onDismiss = {}
)
Spacer(modifier = Modifier.height(10.dp))
InfoSnackBar(
text = "줄 수가 두 줄 이상이 되는 스낵바 메시지입니다. 좌측 정렬을 해주세요.",
onDismiss = {}
)
}
}
}

@Preview(showBackground = true)
@Composable
fun ErrorSnackBarPreview() {
HandyTheme {
Column(
modifier = Modifier.padding(10.dp)
) {
ErrorSnackBar(
text = "에러 메세지가 들어갑니다",
onClick = {}
)
Spacer(modifier = Modifier.height(10.dp))
ErrorSnackBar(
text = "두 줄 이상의 에러 메세지가 들어갈 경우 아이콘은 모두 위로 정렬해주세요.",
onClick = {}
Copy link
Member

@HI-JIN2 HI-JIN2 Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X 버튼 클릭이라는게 좀더 잘 드러나면 어떨까요? onCancel(Click) 이나 onClose(Click) 등.. X 버튼 클릭의 부정적인 느낌이 좀 더 나면 좋을 것 같습니도

)
}
}
}
190 changes: 190 additions & 0 deletions compose/src/main/kotlin/com/yourssu/handy/compose/SnackBar.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package com.yourssu.handy.compose

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.AnchoredDraggableState
import androidx.compose.foundation.gestures.DraggableAnchors
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.anchoredDraggable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
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.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.yourssu.handy.compose.foundation.HandyTypography
import com.yourssu.handy.compose.icons.HandyIcons
import com.yourssu.handy.compose.icons.filled.AlertTriangle
import com.yourssu.handy.compose.icons.line.Close
import kotlin.math.roundToInt

/**
* 드래그 위치를 나타냅니다.
* Start는 초기 위치, End는 스낵바가 사라질 위치를 나타냅니다.
*/
enum class DragValue {
Start, End
}

/**
* 정보성 스낵바의 UI를 그린 함수입니다.
*
* 유저의 행동에 대한 단순 결과를 나타낼 때 사용합니다.
*
* 특정 시간(기본 5초) 노출 후에 자동으로 사라집니다.
* 아래로 스와이프 할 경우에도 사라집니다.
*
* @param text 스낵바의 문구를 나타내는 텍스트, 최대 두 줄까지 입력 가능
* @param modifier Modifier
*/
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
fun InfoSnackBar(
text: String,
onDismiss: () -> Unit,
leeeyubin marked this conversation as resolved.
Show resolved Hide resolved
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
val state = remember {
AnchoredDraggableState(
initialValue = DragValue.Start,
anchors = DraggableAnchors {
with(density) {
DragValue.Start at 0f
DragValue.End at 20.dp.toPx()
}
},
positionalThreshold = { distance: Float -> distance * 0.5f },
velocityThreshold = { with(density) { 50.dp.toPx() } },
animationSpec = tween(),
)
}
val offsetY = remember { Animatable(0f) }

if (state.currentValue == DragValue.End) {
onDismiss()
}

Column(
modifier = modifier
.offset {
IntOffset(
x = 0,
y = offsetY.value.roundToInt()
)
}
.padding(horizontal = 16.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(HandyTheme.colors.snackBarInfo)
.padding(16.dp)
.anchoredDraggable(
state = state,
orientation = Orientation.Vertical,
)
) {
text.split("\n").forEach { line ->
FlowRow {
line.split(" ").forEachIndexed { index, word ->
Text(
text = word,
color = HandyTheme.colors.textBasicWhite,
maxLines = 2,
style = HandyTypography.B3Sb14
)
if (index != line.split(" ").lastIndex) {
Text(
text = " ",
color = HandyTheme.colors.textBasicWhite,
style = HandyTypography.B3Sb14
)
}
}
}
}
}
}

/**
* 에러 스낵바의 UI를 그린 함수입니다.
*
* 사용자의 수행 과정에 부정적인 결과가 발생하거나
* 정보성 스낵바보다 강조해야 할 메시지를 담아야 할 때 사용합니다.
*
* X 버튼을 눌러야만 사라집니다.
*
* @param text 스낵바의 문구를 나타내는 텍스트, 최대 두 줄까지 입력 가능
* @param onClick 스낵바의 X 버튼을 눌렀을 때 호출되는 함수
* @param modifier Modifier
*/
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ErrorSnackBar(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.padding(horizontal = 16.dp)
kangyuri1114 marked this conversation as resolved.
Show resolved Hide resolved
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(HandyTheme.colors.snackBarError)
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = HandyIcons.Filled.AlertTriangle,
tint = HandyTheme.colors.bgStatusNegative,
modifier = Modifier.align(Alignment.Top)
)
text.split("\n").forEach { line ->
FlowRow(modifier = Modifier.weight(1f)) {
line.split(" ").forEachIndexed { index, word ->
Text(
text = word,
color = HandyTheme.colors.textStatusNegative,
maxLines = 2,
style = HandyTypography.B3Sb14
)
if (index != line.split(" ").lastIndex) {
Text(
text = " ",
color = HandyTheme.colors.textStatusNegative,
style = HandyTypography.B3Sb14
)
}
}
}
}
Icon(
imageVector = HandyIcons.Line.Close,
tint = HandyTheme.colors.textBasicTertiary,
modifier = Modifier
.clickable(onClick = onClick)
.align(Alignment.Top)
)
}
}

object SnackBarDefaults {
const val SNACK_BAR_DURATION = 5000L
const val FADE_IN_DURATION = 500
const val FADE_OUT_DURATION = 300
const val TARGET_VALUE = -16f
}
Loading