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

목표 리스트 바텀 시트 기본 UI 구현 #16

Merged
merged 26 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d5f3d5e
feat: add Goal Model
murjune Jan 22, 2025
1e31915
chore: Fake Goals
murjune Jan 22, 2025
9a3cb85
feat: GoalBottomSheet basic Ui
murjune Jan 22, 2025
3094682
feat: subtract to GoalItemCheckBox
murjune Jan 22, 2025
81bfbc5
chore: add todo comment goal state
murjune Jan 22, 2025
05e7d79
feat: goalbottomsheetPreview
murjune Jan 22, 2025
cc21e92
feat: apply GoalPreviewParameterProvider
murjune Jan 22, 2025
2ab1923
feat: change access modifier to internal
murjune Jan 22, 2025
4cb55b3
feat: pinned goal ui
murjune Jan 22, 2025
e86cc42
feat: goalbottomsheet connect to dashBoard
murjune Jan 22, 2025
4c277a8
chore: exclude ktlint datastore-proto
murjune Jan 22, 2025
06cc8a4
style: ktLintFormat
murjune Jan 22, 2025
1add1e6
chore: delete duplicated default parmeters
murjune Jan 22, 2025
f034c57
chore: delete Item postfix name
murjune Jan 22, 2025
413bf01
feat: DobeDobeCheckBox base
murjune Jan 22, 2025
fcf860b
chore: add internal access modifier
murjune Jan 22, 2025
2b92783
refactor: apply bottomSheetScaffold to goalSheet
murjune Jan 22, 2025
e0aa464
refactor: fakeGoals migrate to previewProvider
murjune Jan 22, 2025
5905f9f
refactor: delete modifier GoalBottomSheetHeader
murjune Jan 22, 2025
67991a2
chore: add preview background
murjune Jan 22, 2025
acdaf63
chore: add TODO Comment for method-chain ktlint
murjune Jan 22, 2025
1cc7204
chore: Goal rename to GoalRow
murjune Jan 23, 2025
cab5d76
chore(CheckBox): add lambda boolean params
murjune Jan 23, 2025
8fb2dd9
refactor: GoalRow component :feature:goal
murjune Jan 23, 2025
2aec2ab
chore: GoalContent rename to GoalBottomSheetBody
murjune Jan 23, 2025
3d37881
style: ktFormat
murjune Jan 23, 2025
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
3 changes: 2 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ ktlint_standard_function-expression-body = disabled
ktlint_standard_function-literal = disabled
ktlint_standard_function-type-modifier-spacing = disabled
ktlint_standard_multiline-loop = disabled
ktlint_standard_function-signature = disabled
ktlint_standard_function-signature = disabled
# TODO : compose Modifier 체이닝 메서드 라인 포멧 이상한거 수정
4 changes: 3 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ plugins {
}

allprojects {
apply(plugin = rootProject.libs.plugins.ktlint.get().pluginId)
if (name != "datastore-proto") {
apply(plugin = rootProject.libs.plugins.ktlint.get().pluginId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.chipichipi.dobedobe.core.designsystem.component

import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.BottomSheetScaffoldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp

// TODO : BottomSheetScaffold 컴포넌트 단순 Wrapper 임시 처리, 각 상태 디자인 정의 필요
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DobeDobeBottomSheetScaffold(
sheetContent: @Composable ColumnScope.() -> Unit,
modifier: Modifier = Modifier,
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
sheetPeekHeight: Dp = BottomSheetDefaults.SheetPeekHeight,
sheetDragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
topBar: @Composable (() -> Unit)? = null,
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
content: @Composable (PaddingValues) -> Unit,
) {
BottomSheetScaffold(
sheetContent = sheetContent,
modifier = modifier,
scaffoldState = scaffoldState,
sheetPeekHeight = sheetPeekHeight,
sheetDragHandle = sheetDragHandle,
topBar = topBar,
snackbarHost = snackbarHost,
content = content,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.chipichipi.dobedobe.core.designsystem.component

import androidx.compose.material3.Checkbox
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

// TODO: CheckBox 컴포넌트 단순 Wrapper 임시 처리, 각 상태 디자인 정의 필요
@Composable
fun DobeDobeCheckBox(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)? = null,
modifier: Modifier = Modifier,
enabled: Boolean = true,
) {
Checkbox(
checked = checked,
onCheckedChange = onCheckedChange,
enabled = enabled,
modifier = modifier,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.chipichipi.dobedobe.core.model

data class Goal(
val id: Long,
val title: String,
val isPinned: Boolean,
val state: State,
) {
val isDone: Boolean
get() = state == State.Done

enum class State {
Todo,
Done,
}
}
1 change: 1 addition & 0 deletions feature/dashboard/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ android {

dependencies {
implementation(projects.core.data)
implementation(projects.feature.goal)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.chipichipi.dobedobe.feature.dashboard

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue
import androidx.compose.material3.Text
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState
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.chipichipi.dobedobe.core.designsystem.component.DobeDobeBottomSheetScaffold
import com.chipichipi.dobedobe.feature.dashboard.preview.GoalPreviewParameterProvider
import org.koin.androidx.compose.koinViewModel

@Composable
Expand All @@ -19,16 +25,42 @@ internal fun DashboardRoute(
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DashboardScreen(
onShowSnackbar: suspend (String, String?) -> Boolean,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxSize(),
) {
Text(
"Dashboard",
val bottomSheetScaffoldState =
rememberBottomSheetScaffoldState(
bottomSheetState =
rememberStandardBottomSheetState(
initialValue = SheetValue.PartiallyExpanded,
),
)

DobeDobeBottomSheetScaffold(
modifier = modifier,
scaffoldState = bottomSheetScaffoldState,
sheetContent = {
GoalBottomSheetContent(
goals = GoalPreviewParameterProvider.fakeGoals(20),
// TODO 임시 goal 데이터
onGoalDone = {},
onGoalClicked = {},
)
},
sheetPeekHeight = 200.dp,
// TODO 임시 peekHeight 값
) {
Text("Dashboard")
}
}

@Composable
@Preview
private fun DashboardScreenPreview() {
DashboardScreen(
onShowSnackbar = { _, _ -> false },
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.chipichipi.dobedobe.feature.dashboard

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.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.chipichipi.dobedobe.core.model.Goal
import com.chipichipi.dobedobe.feature.dashboard.preview.GoalPreviewParameterProvider
import com.chipichipi.dobedobe.feature.goal.component.GoalRow

@Composable
internal fun GoalBottomSheetContent(
goals: List<Goal>,
onGoalDone: (Goal) -> Unit,
onGoalClicked: (Goal) -> Unit,
) {
Column {
GoalBottomSheetHeader()
Spacer(modifier = Modifier.height(15.dp))
GoalBottomSheetBody(
goals = goals,
onGoalDone = onGoalDone,
onGoalClicked = onGoalClicked,
)
}
}

@Composable
private fun GoalBottomSheetHeader() {
// TODO: 검색 기능 추가, parameter 는 그때 추가
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
"나의 목표",
// TODO : stringResource 추가 필요
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
)
IconButton(
modifier =
Modifier
.size(42.dp)
.offset {
IntOffset(x = 12.dp.roundToPx(), y = 0)
},
onClick = {},
) {
Icon(
imageVector = Icons.Default.Search,
modifier = Modifier.size(18.dp),
contentDescription = "목표 검색",
// TODO : stringResource 추가 필요
)
}
}
}

@Composable
private fun GoalBottomSheetBody(
goals: List<Goal>,
onGoalDone: (Goal) -> Unit,
onGoalClicked: (Goal) -> Unit,
) {
LazyColumn(
modifier =
Modifier
.fillMaxWidth()
.requiredHeightIn(min = 200.dp),
// TODO: 최소 높이 조절 필요
verticalArrangement = Arrangement.spacedBy(18.dp),
contentPadding = PaddingValues(horizontal = 24.dp),
) {
items(goals) { goal ->
GoalRow(
goal = goal,
onGoalDone = { onGoalDone(goal) },
onGoalClicked = { onGoalClicked(goal) },
)
}
}
}

@Preview(showBackground = true)
@Composable
private fun GoalBottomSheetContentPreview(
@PreviewParameter(GoalPreviewParameterProvider::class) pGoals: List<Goal>,
) {
var goals by remember { mutableStateOf(pGoals) }
val onGoalDone: (Goal) -> Unit = {
goals =
goals.map { goal ->
if (goal.id == it.id) {
return@map if (goal.state == Goal.State.Done) {
goal.copy(state = Goal.State.Todo)
} else {
goal.copy(state = Goal.State.Done)
}
}
goal
}
}
GoalBottomSheetContent(
goals = goals,
onGoalDone = onGoalDone,
onGoalClicked = {},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.chipichipi.dobedobe.feature.dashboard.preview

import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.chipichipi.dobedobe.core.model.Goal

internal class GoalPreviewParameterProvider : PreviewParameterProvider<List<Goal>> {
override val values: Sequence<List<Goal>>
get() =
sequenceOf(
fakeGoals(10),
fakeGoals(3),
fakeGoals(2),
fakeGoals(1),
emptyList(),
)

companion object {
private val fakeGoals: List<Goal> =
listOf(
Goal(1, "3대 500 달성하기", true, Goal.State.Todo),
Goal(2, "영어 단어 하루 10개 외우기", false, Goal.State.Done),
Goal(3, "두비두비 출시", false, Goal.State.Todo),
Goal(4, "주간 러닝 3회", true, Goal.State.Todo),
Goal(5, "하루 2L 물 마시기", false, Goal.State.Todo),
Goal(6, "책 5권 읽기", false, Goal.State.Todo),
Goal(7, "일본 여행 다녀오기", false, Goal.State.Done),
Goal(8, "운동 루틴 정착", true, Goal.State.Done),
)

fun fakeGoals(size: Int): List<Goal> =
List(size) {
fakeGoals[it % fakeGoals.size].copy(id = it.toLong())
}
}
}
Loading
Loading