diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/NekiTextField.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/NekiTextField.kt
new file mode 100644
index 000000000..f897dd6dd
--- /dev/null
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/NekiTextField.kt
@@ -0,0 +1,279 @@
+package com.neki.android.core.designsystem
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldState
+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.graphics.Brush
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+fun NekiTextField(
+ textFieldState: TextFieldState,
+ modifier: Modifier = Modifier,
+ titleLabel: String? = null,
+ placeholder: String = "",
+ maxLength: Int? = null,
+ isError: Boolean = false,
+ textStyle: TextStyle = NekiTheme.typography.body16Medium.copy(
+ color = NekiTheme.colorScheme.gray900,
+ ),
+ cursorBrush: Brush = SolidColor(NekiTheme.colorScheme.gray800),
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.SingleLine,
+ inputTransformation: InputTransformation? = null,
+ outputTransformation: OutputTransformation? = null,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isFocused by interactionSource.collectIsFocusedAsState()
+
+ val borderColor = when {
+ isError -> NekiTheme.colorScheme.primary600
+ isFocused -> NekiTheme.colorScheme.gray700
+ else -> NekiTheme.colorScheme.gray75
+ }
+
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ ) {
+ titleLabel?.let {
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 2.dp),
+ text = it,
+ style = NekiTheme.typography.body14Medium,
+ color = NekiTheme.colorScheme.gray700,
+ )
+ }
+
+ BasicTextField(
+ state = textFieldState,
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = NekiTheme.colorScheme.white,
+ shape = RoundedCornerShape(8.dp),
+ )
+ .border(
+ width = 1.dp,
+ color = borderColor,
+ shape = RoundedCornerShape(8.dp),
+ )
+ .padding(horizontal = 16.dp, vertical = 13.dp),
+ textStyle = textStyle,
+ inputTransformation = inputTransformation,
+ outputTransformation = outputTransformation,
+ interactionSource = interactionSource,
+ cursorBrush = cursorBrush,
+ lineLimits = lineLimits,
+ keyboardOptions = keyboardOptions,
+ decorator = { innerTextField ->
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(modifier = Modifier.weight(1f)) {
+ if (textFieldState.text.isEmpty()) {
+ Text(
+ text = placeholder,
+ style = NekiTheme.typography.body16Regular,
+ color = NekiTheme.colorScheme.gray300,
+ )
+ }
+ innerTextField()
+ }
+ maxLength?.let {
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = "${textFieldState.text.length}/$maxLength",
+ style = NekiTheme.typography.caption12Regular,
+ color = NekiTheme.colorScheme.gray300,
+ )
+ }
+ }
+ },
+ )
+ }
+}
+
+@Composable
+fun NekiTextFieldWithError(
+ textFieldState: TextFieldState,
+ modifier: Modifier = Modifier,
+ titleLabel: String? = null,
+ placeholder: String = "",
+ maxLength: Int? = null,
+ isError: Boolean = false,
+ errorMessage: String? = null,
+ textStyle: TextStyle = NekiTheme.typography.body16Medium.copy(
+ color = NekiTheme.colorScheme.gray900,
+ ),
+ cursorBrush: Brush = SolidColor(NekiTheme.colorScheme.gray800),
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.SingleLine,
+ inputTransformation: InputTransformation? = null,
+ outputTransformation: OutputTransformation? = null,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ ) {
+ NekiTextField(
+ textFieldState = textFieldState,
+ titleLabel = titleLabel,
+ placeholder = placeholder,
+ maxLength = maxLength,
+ isError = isError,
+ textStyle = textStyle,
+ cursorBrush = cursorBrush,
+ lineLimits = lineLimits,
+ inputTransformation = inputTransformation,
+ outputTransformation = outputTransformation,
+ keyboardOptions = keyboardOptions,
+ )
+
+ Text(
+ modifier = Modifier.heightIn(min = 16.dp),
+ text = if (isError) errorMessage.orEmpty() else "",
+ style = NekiTheme.typography.caption12Regular,
+ color = NekiTheme.colorScheme.primary600,
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun NekiTextFieldPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ NekiTextField(
+ textFieldState = remember { TextFieldState() },
+ placeholder = "플레이스홀더",
+ )
+
+ NekiTextField(
+ textFieldState = remember { TextFieldState("입력된 텍스트") },
+ maxLength = 20,
+ )
+
+ NekiTextField(
+ textFieldState = remember { TextFieldState("에러 상태") },
+ isError = true,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun NekiTextFieldWithTitleLabelPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ NekiTextField(
+ textFieldState = remember { TextFieldState() },
+ titleLabel = "닉네임",
+ placeholder = "닉네임을 입력해주세요",
+ )
+
+ NekiTextField(
+ textFieldState = remember { TextFieldState("입력된 텍스트") },
+ titleLabel = "닉네임",
+ maxLength = 10,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun NekiTextFieldWithErrorPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState() },
+ placeholder = "플레이스홀더",
+ )
+
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState("입력된 텍스트") },
+ placeholder = "플레이스홀더",
+ )
+
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState("에러 상태") },
+ isError = true,
+ errorMessage = "올바른 값을 입력해주세요",
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun NekiTextFieldWithErrorAndTitleLabelPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState() },
+ titleLabel = "닉네임",
+ placeholder = "닉네임을 입력해주세요",
+ maxLength = 10,
+ )
+
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState("입력된 텍스트") },
+ placeholder = "플레이스홀더",
+ maxLength = 10,
+ )
+
+ NekiTextFieldWithError(
+ textFieldState = remember { TextFieldState("에러 상태") },
+ titleLabel = "닉네임",
+ isError = true,
+ errorMessage = "이미 사용 중인 닉네임입니다",
+ maxLength = 10,
+ )
+ }
+ }
+}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/actionbar/NekiActionBar.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/actionbar/NekiActionBar.kt
new file mode 100644
index 000000000..1960f1a62
--- /dev/null
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/actionbar/NekiActionBar.kt
@@ -0,0 +1,167 @@
+package com.neki.android.core.designsystem.actionbar
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.button.NekiIconButton
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+fun NekiStartActionBar(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ HorizontalDivider(
+ modifier = Modifier.fillMaxWidth(),
+ thickness = 1.dp,
+ color = NekiTheme.colorScheme.gray75,
+ )
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.CenterStart,
+ ) {
+ content()
+ }
+ }
+}
+
+@Composable
+fun NekiEndActionBar(
+ modifier: Modifier = Modifier,
+ content: @Composable () -> Unit,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ HorizontalDivider(
+ modifier = Modifier.fillMaxWidth(),
+ thickness = 1.dp,
+ color = NekiTheme.colorScheme.gray75,
+ )
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.CenterEnd,
+ ) {
+ content()
+ }
+ }
+}
+
+@Composable
+fun NekiBothSidesActionBar(
+ modifier: Modifier = Modifier,
+ startContent: @Composable () -> Unit,
+ endContent: @Composable () -> Unit,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ HorizontalDivider(
+ modifier = Modifier.fillMaxWidth(),
+ thickness = 1.dp,
+ color = NekiTheme.colorScheme.gray75,
+ )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ startContent()
+ endContent()
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun NekiStartActionBarPreview() {
+ NekiTheme {
+ NekiStartActionBar(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ NekiIconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = {},
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_arrow_left),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.gray900,
+ )
+ }
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun NekiEndActionBarPreview() {
+ NekiTheme {
+ NekiEndActionBar(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ NekiIconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = {},
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_scrap_stroked),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.gray500,
+ )
+ }
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun NekiBothSidesActionBarPreview() {
+ NekiTheme {
+ NekiBothSidesActionBar(
+ modifier = Modifier.fillMaxWidth(),
+ startContent = {
+ NekiIconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = {},
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_arrow_left),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.gray900,
+ )
+ }
+ },
+ endContent = {
+ NekiIconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = {},
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_scrap_stroked),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.gray500,
+ )
+ }
+ },
+ )
+ }
+}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/bottomsheet/NekiTextFieldBottomSheet.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/bottomsheet/NekiTextFieldBottomSheet.kt
index a57a89977..9043fb1cf 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/bottomsheet/NekiTextFieldBottomSheet.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/bottomsheet/NekiTextFieldBottomSheet.kt
@@ -1,26 +1,15 @@
package com.neki.android.core.designsystem.bottomsheet
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.heightIn
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.foundation.text.input.InputTransformation
-import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.TextFieldState
-import androidx.compose.foundation.text.input.maxLength
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
@@ -28,14 +17,10 @@ import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
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.graphics.SolidColor
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.min
import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.NekiTextFieldWithError
import com.neki.android.core.designsystem.button.CTAButtonGray
import com.neki.android.core.designsystem.button.CTAButtonPrimary
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -124,20 +109,13 @@ private fun NekiTextFieldBottomSheetContent(
Column(
verticalArrangement = Arrangement.spacedBy(6.dp),
) {
- NekiBottomSheetTextField(
+ NekiTextFieldWithError(
textFieldState = textFieldState,
placeholder = placeholder,
maxLength = maxLength,
isError = isError,
keyboardOptions = keyboardOptions,
- )
-
- // Error message
- Text(
- modifier = Modifier.heightIn(min = 16.dp),
- text = if (isError) errorMessage.orEmpty() else "",
- style = NekiTheme.typography.caption12Regular,
- color = NekiTheme.colorScheme.primary600,
+ errorMessage = errorMessage,
)
}
@@ -163,75 +141,6 @@ private fun NekiTextFieldBottomSheetContent(
}
}
-@Composable
-private fun NekiBottomSheetTextField(
- textFieldState: TextFieldState,
- modifier: Modifier = Modifier,
- placeholder: String = "",
- maxLength: Int? = null,
- isError: Boolean = false,
- keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
-) {
- val interactionSource = remember { MutableInteractionSource() }
- val isFocused by interactionSource.collectIsFocusedAsState()
-
- val borderColor = when {
- isError -> NekiTheme.colorScheme.primary600
- isFocused -> NekiTheme.colorScheme.gray700
- else -> NekiTheme.colorScheme.gray75
- }
-
- BasicTextField(
- state = textFieldState,
- modifier = modifier
- .fillMaxWidth()
- .background(
- color = NekiTheme.colorScheme.white,
- shape = RoundedCornerShape(8.dp),
- )
- .border(
- width = 1.dp,
- color = borderColor,
- shape = RoundedCornerShape(8.dp),
- )
- .padding(horizontal = 16.dp, vertical = 13.dp),
- textStyle = NekiTheme.typography.body16Medium.copy(
- color = NekiTheme.colorScheme.gray900,
- ),
- inputTransformation = maxLength?.let { InputTransformation.maxLength(it) },
- interactionSource = interactionSource,
- cursorBrush = SolidColor(NekiTheme.colorScheme.gray800),
- lineLimits = TextFieldLineLimits.SingleLine,
- keyboardOptions = keyboardOptions,
- decorator = { innerTextField ->
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Box(modifier = Modifier.weight(1f)) {
- if (textFieldState.text.isEmpty()) {
- Text(
- text = placeholder,
- style = NekiTheme.typography.body16Regular,
- color = NekiTheme.colorScheme.gray300,
- )
- }
- innerTextField()
- }
- maxLength?.let {
- Spacer(modifier = Modifier.width(4.dp))
- Text(
- text = "${textFieldState.text.length}/$maxLength",
- style = NekiTheme.typography.caption12Regular,
- color = NekiTheme.colorScheme.gray300,
- )
- }
- }
- },
- )
-}
-
@ComponentPreview
@Composable
private fun NekiTextFieldBottomSheetContentDefaultPreview() {
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/NekiTextButton.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/NekiTextButton.kt
index cc9062c67..aaccab371 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/NekiTextButton.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/NekiTextButton.kt
@@ -2,13 +2,12 @@ package com.neki.android.core.designsystem.button
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.graphics.Color
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.modifier.MultipleEventsCutter
import com.neki.android.core.designsystem.modifier.get
@@ -20,6 +19,8 @@ fun NekiTextButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
multipleEventsCutterEnabled: Boolean = true,
+ enabledTextColor: Color = NekiTheme.colorScheme.primary500,
+ disabledTextColor: Color = NekiTheme.colorScheme.gray200,
contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
content: @Composable () -> Unit = {},
) {
@@ -36,8 +37,8 @@ fun NekiTextButton(
contentPadding = contentPadding,
enabled = enabled,
colors = ButtonDefaults.textButtonColors(
- contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
- disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ contentColor = enabledTextColor,
+ disabledContentColor = disabledTextColor,
),
) {
content()
@@ -52,8 +53,7 @@ private fun NekiTextButtonPreview() {
onClick = {},
) {
Text(
- text = "Text Button",
- textDecoration = TextDecoration.Underline,
+ text = "텍스트버튼",
)
}
}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/TopBarTextButton.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/TopBarTextButton.kt
deleted file mode 100644
index 252b204ca..000000000
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/button/TopBarTextButton.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.neki.android.core.designsystem.button
-
-import androidx.compose.foundation.layout.offset
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.LayoutDirection
-import com.neki.android.core.designsystem.ComponentPreview
-import com.neki.android.core.designsystem.ui.theme.NekiTheme
-
-@Composable
-fun TopBarTextButton(
- buttonText: String,
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- enabledTextColor: Color = NekiTheme.colorScheme.primary500,
- disabledTextColor: Color = NekiTheme.colorScheme.gray200,
- onClick: () -> Unit = {},
-) {
- NekiTextButton(
- modifier = modifier.offset(
- x = ButtonDefaults.TextButtonContentPadding.calculateLeftPadding(LayoutDirection.Ltr),
- ),
- onClick = onClick,
- enabled = enabled,
- ) {
- Text(
- text = buttonText,
- style = NekiTheme.typography.body16SemiBold,
- color = if (enabled) enabledTextColor else disabledTextColor,
- )
- }
-}
-
-@ComponentPreview
-@Composable
-private fun EnabledTopBarTextButtonPreview() {
- NekiTheme {
- TopBarTextButton(
- buttonText = "텍스트버튼",
- onClick = {},
- )
- }
-}
-
-@ComponentPreview
-@Composable
-private fun DisabledTopBarTextButtonPreview() {
- NekiTheme {
- TopBarTextButton(
- buttonText = "텍스트버튼",
- onClick = {},
- enabled = false,
- )
- }
-}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index df3370121..626dbe6d8 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -1,54 +1,14 @@
package com.neki.android.core.designsystem.modifier
import androidx.compose.foundation.background
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
-/**
- * 사진 컴포넌트에 적용되는 그라데이션 배경
- * 좌하단에서 우상단으로 갈수록 어두워지는 효과
- */
-fun Modifier.photoBackground(
- shape: Shape = RoundedCornerShape(12.dp),
-): Modifier = this.background(
- brush = Brush.linearGradient(
- colorStops = arrayOf(
- 0f to Color.Black.copy(alpha = 0f),
- 0.7f to Color.Black.copy(alpha = 0.09f),
- 1f to Color.Black.copy(alpha = 0.3f),
- ),
- start = Offset(0f, Float.POSITIVE_INFINITY),
- end = Offset(Float.POSITIVE_INFINITY, 0f),
- ),
- shape = shape,
-)
-
-/**
- * 포즈 컴포넌트에 적용되는 그라데이션 배경
- * 상단에서 134/242 지점까지 어두워지는 효과
- */
-fun Modifier.poseBackground(
- shape: Shape = RoundedCornerShape(12.dp),
-): Modifier = this.background(
- brush = Brush.verticalGradient(
- colorStops = arrayOf(
- 0f to Color.Black.copy(alpha = 0.2f),
- 134f / 242f to Color.Black.copy(alpha = 0f),
- ),
- ),
- shape = shape,
-)
-
/**
* 블러 효과가 적용된 배경을 설정하는 Modifier 확장 함수
*
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Stroke.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Stroke.kt
new file mode 100644
index 000000000..d9940d3d3
--- /dev/null
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Stroke.kt
@@ -0,0 +1,30 @@
+package com.neki.android.core.designsystem.modifier
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+fun Modifier.dashStroke(
+ color: Color,
+ strokeWidth: Dp = 1.dp,
+ dashLength: Dp = 5.dp,
+ gapLength: Dp = 5.dp,
+ cornerRadius: Dp = 0.dp,
+): Modifier = this.drawBehind {
+ drawRoundRect(
+ color = color,
+ style = Stroke(
+ width = strokeWidth.toPx(),
+ pathEffect = PathEffect.dashPathEffect(
+ intervals = floatArrayOf(dashLength.toPx(), gapLength.toPx()),
+ phase = 0f,
+ ),
+ ),
+ cornerRadius = CornerRadius(cornerRadius.toPx()),
+ )
+}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
index 1ea95a9bd..f3c402875 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
@@ -2,33 +2,57 @@ package com.neki.android.core.designsystem.popup
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.modifier.noRippleClickableSingle
import com.neki.android.core.designsystem.ui.theme.NekiTheme
+private const val DEFAULT_ARROW_POSITION = 16
+
+enum class ArrowDirection {
+ Up, Down,
+}
+
+enum class ToolTipColor {
+ Gray800, Gray25
+}
+
@Composable
fun ToolTipPopup(
tooltipText: String,
- color: Color,
offset: IntOffset,
+ arrowDirection: ArrowDirection,
alignment: Alignment,
- onDismissRequest: () -> Unit,
+ arrowAlignment: Alignment,
+ arrowPosition: Dp = DEFAULT_ARROW_POSITION.dp,
+ toolTipColor: ToolTipColor = ToolTipColor.Gray800,
+ hasCloseButton: Boolean = false,
+ onDismissRequest: () -> Unit = {},
+ onClickCloseButton: () -> Unit = {},
) {
Popup(
alignment = alignment,
@@ -37,80 +61,321 @@ fun ToolTipPopup(
) {
ToolTipContent(
tooltipText = tooltipText,
- color = color,
+ toolTipColor = toolTipColor,
+ arrowDirection = arrowDirection,
+ arrowPosition = arrowPosition,
+ arrowAlignment = arrowAlignment,
+ hasCloseButton = hasCloseButton,
+ onClickCloseButton = onClickCloseButton,
)
}
}
+@Composable
+private fun TriangleArrow(
+ direction: ArrowDirection,
+ color: Color,
+ modifier: Modifier = Modifier,
+ width: Dp = 10.dp,
+ height: Dp = 8.dp,
+) {
+ Canvas(
+ modifier = modifier.size(width = width, height = height),
+ ) {
+ val cornerRadius = 1.dp.toPx()
+ val path = when (direction) {
+ ArrowDirection.Up -> Path().apply {
+ moveTo(0f, size.height)
+ lineTo(size.width / 2 - cornerRadius, cornerRadius)
+ quadraticTo(size.width / 2, 0f, size.width / 2 + cornerRadius, cornerRadius)
+ lineTo(size.width, size.height)
+ close()
+ }
+
+ ArrowDirection.Down -> Path().apply {
+ moveTo(0f, 0f)
+ lineTo(size.width / 2 - cornerRadius, size.height - cornerRadius)
+ quadraticTo(size.width / 2, size.height, size.width / 2 + cornerRadius, size.height - cornerRadius)
+ lineTo(size.width, 0f)
+ close()
+ }
+ }
+ drawPath(path, color)
+ }
+}
+
@Composable
private fun ToolTipContent(
tooltipText: String,
- color: Color,
+ arrowDirection: ArrowDirection,
+ arrowAlignment: Alignment,
modifier: Modifier = Modifier,
+ toolTipColor: ToolTipColor = ToolTipColor.Gray800,
+ arrowPosition: Dp = DEFAULT_ARROW_POSITION.dp,
+ hasCloseButton: Boolean = false,
+ onClickCloseButton: () -> Unit = {},
) {
+ val backgroundColor = when (toolTipColor) {
+ ToolTipColor.Gray800 -> NekiTheme.colorScheme.gray800
+ ToolTipColor.Gray25 -> NekiTheme.colorScheme.gray25
+ }
+ val textColor = when (toolTipColor) {
+ ToolTipColor.Gray800 -> NekiTheme.colorScheme.white
+ ToolTipColor.Gray25 -> NekiTheme.colorScheme.gray900
+ }
+ val iconTint = when (toolTipColor) {
+ ToolTipColor.Gray800 -> NekiTheme.colorScheme.gray25
+ ToolTipColor.Gray25 -> NekiTheme.colorScheme.gray500
+ }
+
Column(
modifier = modifier.width(IntrinsicSize.Max),
) {
- // 꼬리 (오른쪽 정렬, 오른쪽에서 16dp)
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(end = 16.dp),
- contentAlignment = Alignment.CenterEnd,
- ) {
- Canvas(
- modifier = Modifier.size(width = 10.dp, height = 8.dp),
- ) {
- val cornerRadius = 1.dp.toPx()
- val path = Path().apply {
- // 왼쪽 하단에서 시작
- moveTo(0f, size.height)
- // 왼쪽 하단 -> 꼭대기 (둥근 모서리)
- lineTo(
- size.width / 2 - cornerRadius,
- cornerRadius,
- )
- quadraticTo(
- size.width / 2,
- 0f,
- size.width / 2 + cornerRadius,
- cornerRadius,
- )
- // 꼭대기 -> 오른쪽 하단
- lineTo(size.width, size.height)
- close()
- }
- drawPath(path, color)
- }
+ if (arrowDirection == ArrowDirection.Up) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = arrowPosition),
+ contentAlignment = arrowAlignment,
+ ) { TriangleArrow(direction = arrowDirection, color = backgroundColor) }
}
-
- // 몸통
- Box(
+ Row(
modifier = Modifier
- .background(
- color = color,
- shape = RoundedCornerShape(8.dp),
- )
+ .background(color = backgroundColor, shape = RoundedCornerShape(8.dp))
.padding(horizontal = 12.dp, vertical = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(2.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = tooltipText,
style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.white,
+ color = textColor,
)
+ if (hasCloseButton) {
+ Icon(
+ modifier = Modifier
+ .size(16.dp)
+ .noRippleClickableSingle(onClick = onClickCloseButton),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_close),
+ tint = iconTint,
+ contentDescription = null,
+ )
+ }
+ }
+ if (arrowDirection == ArrowDirection.Down) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = arrowPosition),
+ contentAlignment = arrowAlignment,
+ ) { TriangleArrow(direction = arrowDirection, color = backgroundColor) }
}
}
}
@ComponentPreview
@Composable
-private fun ToolTipPopupPreview() {
+private fun ToolTipArrowUpPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterStart,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.Center,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterEnd,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterStart,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.Center,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterEnd,
+ hasCloseButton = true,
+ )
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun ToolTipArrowDownPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterStart,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.Center,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterEnd,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterStart,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.Center,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterEnd,
+ hasCloseButton = true,
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ToolTipArrowUpGrayPreview() {
+ NekiTheme {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterStart,
+ toolTipColor = ToolTipColor.Gray25,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.Center,
+ toolTipColor = ToolTipColor.Gray25,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterEnd,
+ toolTipColor = ToolTipColor.Gray25,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterStart,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.Center,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterEnd,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ToolTipArrowDownGrayPreview() {
NekiTheme {
- Box(modifier = Modifier.padding(16.dp)) {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterStart,
+ toolTipColor = ToolTipColor.Gray25,
+ )
ToolTipContent(
- tooltipText = "툴팁 메시지입니다",
- color = NekiTheme.colorScheme.gray800,
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.Center,
+ toolTipColor = ToolTipColor.Gray25,
)
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterEnd,
+ toolTipColor = ToolTipColor.Gray25,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterStart,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.Center,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ ToolTipContent(
+ tooltipText = "텍스트",
+ arrowDirection = ArrowDirection.Down,
+ arrowAlignment = Alignment.CenterEnd,
+ toolTipColor = ToolTipColor.Gray25,
+ hasCloseButton = true,
+ )
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun TriangleArrowPreview() {
+ NekiTheme {
+ Row(
+ modifier = Modifier.padding(16.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ TriangleArrow(direction = ArrowDirection.Up, color = NekiTheme.colorScheme.gray800)
+ TriangleArrow(direction = ArrowDirection.Down, color = NekiTheme.colorScheme.gray800)
}
}
}
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/topbar/NekiTopBar.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/topbar/NekiTopBar.kt
index d27b24b7a..e335eb3aa 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/topbar/NekiTopBar.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/topbar/NekiTopBar.kt
@@ -35,37 +35,37 @@ fun NekiTitleTopBar(
}
@Composable
-fun NekiLeftTitleTopBar(
+private fun NekiTopBar(
modifier: Modifier = Modifier,
- title: @Composable (() -> Unit)? = null,
- actions: @Composable (() -> Unit)? = null,
+ title: @Composable ((Modifier) -> Unit)? = null,
+ leadingIcon: @Composable ((Modifier) -> Unit)? = null,
+ actions: @Composable ((Modifier) -> Unit)? = null,
) {
- Row(
+ Box(
modifier = modifier
.fillMaxWidth()
.height(54.dp),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween,
) {
- title?.invoke()
- actions?.invoke()
+ leadingIcon?.invoke(Modifier.align(Alignment.CenterStart))
+ title?.invoke(Modifier.align(Alignment.Center))
+ actions?.invoke(Modifier.align(Alignment.CenterEnd))
}
}
@Composable
-private fun NekiTopBar(
+fun NekiLeftTitleTopBar(
modifier: Modifier = Modifier,
- title: @Composable ((Modifier) -> Unit)? = null,
- leadingIcon: @Composable ((Modifier) -> Unit)? = null,
- actions: @Composable ((Modifier) -> Unit)? = null,
+ title: @Composable (() -> Unit)? = null,
+ actions: @Composable (() -> Unit)? = null,
) {
- Box(
+ Row(
modifier = modifier
.fillMaxWidth()
.height(54.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
) {
- leadingIcon?.invoke(Modifier.align(Alignment.CenterStart))
- title?.invoke(Modifier.align(Alignment.Center))
- actions?.invoke(Modifier.align(Alignment.CenterEnd))
+ title?.invoke()
+ actions?.invoke()
}
}
diff --git a/core/designsystem/src/main/res/drawable/icon_empty_gallery_thumbnail.xml b/core/designsystem/src/main/res/drawable/icon_empty_gallery_thumbnail.xml
new file mode 100644
index 000000000..7705b019e
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/icon_empty_gallery_thumbnail.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/image_empty_folder.png b/core/designsystem/src/main/res/drawable/image_empty_folder.png
new file mode 100644
index 000000000..c1e6dfed6
Binary files /dev/null and b/core/designsystem/src/main/res/drawable/image_empty_folder.png differ
diff --git a/core/designsystem/src/main/res/drawable/image_empty_photo.png b/core/designsystem/src/main/res/drawable/image_empty_photo.png
new file mode 100644
index 000000000..0ac5e57d5
Binary files /dev/null and b/core/designsystem/src/main/res/drawable/image_empty_photo.png differ
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
index ac9141969..f5c326e17 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
@@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable
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.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
@@ -41,6 +42,7 @@ fun FavoriteAlbumRowComponent(
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
FavoriteAlbumThumbnail(
+ isEmpty = album.photoCount == 0,
thumbnailUrl = album.thumbnailUrl,
)
@@ -68,6 +70,7 @@ fun AlbumRowComponent(
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
AlbumThumbnail(
+ isEmpty = album.photoCount == 0,
thumbnailUrl = album.thumbnailUrl,
)
@@ -88,6 +91,7 @@ fun AlbumRowComponent(
@Composable
private fun FavoriteAlbumThumbnail(
thumbnailUrl: String?,
+ isEmpty: Boolean,
modifier: Modifier = Modifier,
) {
Box(
@@ -96,45 +100,81 @@ private fun FavoriteAlbumThumbnail(
.clip(RoundedCornerShape(8.dp)),
contentAlignment = Alignment.Center,
) {
- AsyncImage(
- modifier = Modifier.matchParentSize(),
- model = thumbnailUrl,
- contentDescription = null,
- contentScale = ContentScale.Crop,
- )
+ if (isEmpty || thumbnailUrl == null) {
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .background(NekiTheme.colorScheme.gray50),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_empty_gallery_thumbnail),
+ tint = NekiTheme.colorScheme.gray100,
+ contentDescription = null,
+ )
+ }
+ } else {
+ AsyncImage(
+ modifier = Modifier
+ .background(color = Color.Black.copy(alpha = 0.04f))
+ .matchParentSize(),
+ model = thumbnailUrl,
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ )
- Box(
- modifier = Modifier
- .matchParentSize()
- .background(NekiTheme.colorScheme.favoriteAlbumCover.copy(alpha = 0.5f)),
- )
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .background(NekiTheme.colorScheme.favoriteAlbumCover.copy(alpha = 0.5f)),
+ )
- Icon(
- modifier = Modifier.size(20.dp),
- imageVector = ImageVector.vectorResource(R.drawable.icon_heart_filled),
- contentDescription = null,
- tint = NekiTheme.colorScheme.white,
- )
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_heart_filled),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.white,
+ )
+ }
}
}
@Composable
private fun AlbumThumbnail(
thumbnailUrl: String?,
+ isEmpty: Boolean,
modifier: Modifier = Modifier,
) {
- AsyncImage(
- modifier = modifier
- .size(72.dp)
- .clip(RoundedCornerShape(8.dp))
- .background(
- color = NekiTheme.colorScheme.gray50,
- shape = RoundedCornerShape(8.dp),
- ),
- model = thumbnailUrl,
- contentDescription = null,
- contentScale = ContentScale.Crop,
- )
+ if (isEmpty || thumbnailUrl == null) {
+ Box(
+ modifier = Modifier
+ .size(72.dp)
+ .clip(RoundedCornerShape(8.dp))
+ .background(NekiTheme.colorScheme.gray50),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_empty_gallery_thumbnail),
+ tint = NekiTheme.colorScheme.gray100,
+ contentDescription = null,
+ )
+ }
+ } else {
+ AsyncImage(
+ modifier = modifier
+ .size(72.dp)
+ .clip(RoundedCornerShape(8.dp))
+ .background(
+ color = NekiTheme.colorScheme.gray50,
+ shape = RoundedCornerShape(8.dp),
+ ),
+ model = thumbnailUrl,
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ )
+ }
}
@Composable
@@ -165,12 +205,23 @@ private fun AlbumInfo(
@Composable
private fun FavoriteAlbumRowComponentPreview() {
NekiTheme {
- FavoriteAlbumRowComponent(
- album = AlbumPreview(
- id = 0,
- title = "즐겨찾기",
- ),
- )
+ Column {
+ FavoriteAlbumRowComponent(
+ album = AlbumPreview(
+ id = 0,
+ title = "즐겨찾기",
+ photoCount = 0,
+ ),
+ )
+ FavoriteAlbumRowComponent(
+ album = AlbumPreview(
+ id = 0,
+ title = "즐겨찾기",
+ thumbnailUrl = "https://example.com/photo.jpg",
+ photoCount = 12,
+ ),
+ )
+ }
}
}
@@ -178,12 +229,23 @@ private fun FavoriteAlbumRowComponentPreview() {
@Composable
private fun AlbumRowComponentPreview() {
NekiTheme {
- AlbumRowComponent(
- album = AlbumPreview(
- id = 1,
- title = "일반앨범제목",
- ),
- )
+ Column {
+ AlbumRowComponent(
+ album = AlbumPreview(
+ id = 1,
+ title = "빈 앨범",
+ photoCount = 0,
+ ),
+ )
+ AlbumRowComponent(
+ album = AlbumPreview(
+ id = 2,
+ title = "일반앨범제목",
+ thumbnailUrl = "https://example.com/photo.jpg",
+ photoCount = 5,
+ ),
+ )
+ }
}
}
@@ -191,20 +253,22 @@ private fun AlbumRowComponentPreview() {
@Composable
private fun AlbumRowComponentSelectablePreview() {
NekiTheme {
- Column(verticalArrangement = Arrangement.spacedBy(20.dp)) {
+ Column {
AlbumRowComponent(
album = AlbumPreview(
id = 1,
- title = "선택되지 않은 앨범",
+ title = "선택되지 않은 빈 앨범",
+ photoCount = 0,
),
isSelectable = true,
isSelected = false,
)
-
AlbumRowComponent(
album = AlbumPreview(
id = 2,
title = "선택된 앨범",
+ thumbnailUrl = "https://example.com/photo.jpg",
+ photoCount = 8,
),
isSelectable = true,
isSelected = true,
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/DoubleButtonOptionBottomSheet.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/DoubleButtonOptionBottomSheet.kt
index 5be1d7652..a34b200b1 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/DoubleButtonOptionBottomSheet.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/DoubleButtonOptionBottomSheet.kt
@@ -26,6 +26,7 @@ import com.neki.android.core.designsystem.button.CTAButtonGray
import com.neki.android.core.designsystem.button.CTAButtonPrimary
import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.core.ui.compose.VerticalSpacer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@@ -83,18 +84,17 @@ internal fun DoubleButtonOptionBottomSheetContent(
Column(
modifier = modifier
.fillMaxWidth()
- .padding(horizontal = 20.dp)
- .padding(bottom = 34.dp),
- verticalArrangement = Arrangement.spacedBy(16.dp),
+ .padding(top = 4.dp, bottom = 34.dp),
) {
Text(
+ modifier = Modifier.padding(horizontal = 20.dp),
text = title,
style = NekiTheme.typography.title20SemiBold,
color = NekiTheme.colorScheme.gray900,
)
-
+ VerticalSpacer(12.dp)
Column(
- verticalArrangement = Arrangement.spacedBy(4.dp),
+ modifier = Modifier.fillMaxWidth(),
) {
options.forEach { option ->
OptionRow(
@@ -104,9 +104,12 @@ internal fun DoubleButtonOptionBottomSheetContent(
)
}
}
+ VerticalSpacer(16.dp)
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
CTAButtonGray(
@@ -136,7 +139,7 @@ private fun OptionRow(
modifier = modifier
.fillMaxWidth()
.noRippleClickable(onClick = onClick)
- .padding(vertical = 12.dp),
+ .padding(horizontal = 20.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/DropdownPopup.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/DropdownPopup.kt
new file mode 100644
index 000000000..934688057
--- /dev/null
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/DropdownPopup.kt
@@ -0,0 +1,87 @@
+package com.neki.android.core.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupProperties
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.modifier.clickableSingle
+import com.neki.android.core.designsystem.modifier.dropdownShadow
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.toImmutableList
+
+@Composable
+fun DropdownPopup(
+ items: ImmutableList,
+ selectedItem: T?,
+ onSelect: (T) -> Unit,
+ onDismissRequest: () -> Unit,
+ itemLabel: (T) -> String,
+ modifier: Modifier = Modifier,
+ offset: IntOffset = IntOffset.Zero,
+ alignment: Alignment = Alignment.TopStart,
+) {
+ Popup(
+ offset = offset,
+ alignment = alignment,
+ onDismissRequest = onDismissRequest,
+ properties = PopupProperties(focusable = true),
+ ) {
+ Column(
+ modifier = modifier
+ .dropdownShadow(shape = RoundedCornerShape(8.dp))
+ .background(
+ color = NekiTheme.colorScheme.white,
+ shape = RoundedCornerShape(8.dp),
+ )
+ .padding(vertical = 6.dp),
+ ) {
+ items.forEach { item ->
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = if (selectedItem == item) NekiTheme.colorScheme.gray50
+ else NekiTheme.colorScheme.white,
+ )
+ .clickableSingle { onSelect(item) }
+ .padding(horizontal = 16.dp, vertical = 5.dp),
+ text = itemLabel(item),
+ style = NekiTheme.typography.body14Medium,
+ )
+ }
+ }
+ }
+}
+
+private enum class PreviewDropdownOption(val label: String) {
+ NEWEST("최신순"),
+ OLDEST("오래된순"),
+ ;
+
+ override fun toString(): String = label
+}
+
+@ComponentPreview
+@Composable
+private fun DropdownPopupPreview() {
+ NekiTheme {
+ DropdownPopup(
+ items = PreviewDropdownOption.entries.toImmutableList(),
+ selectedItem = PreviewDropdownOption.NEWEST,
+ onSelect = {},
+ onDismissRequest = {},
+ itemLabel = { it.label },
+ )
+ }
+}
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/FilterBar.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/FilterBar.kt
index a36067187..686333958 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/FilterBar.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/FilterBar.kt
@@ -15,6 +15,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
@@ -31,6 +32,7 @@ fun FilterBar(
defaultChipDisplayText: String,
modifier: Modifier = Modifier,
visible: Boolean = true,
+ chipShape: Shape = CircleShape,
onClickDownIconChip: () -> Unit = {},
onClickDefaultChip: () -> Unit = {},
) {
@@ -50,11 +52,13 @@ fun FilterBar(
DownIconFilterChip(
isSelected = isDownIconChipSelected,
displayText = downIconChipDisplayText,
+ chipShape = chipShape,
onClick = onClickDownIconChip,
)
DefaultFilterChip(
isSelected = isDefaultChipSelected,
displayText = defaultChipDisplayText,
+ chipShape = chipShape,
onClick = onClickDefaultChip,
)
}
@@ -66,12 +70,13 @@ private fun DownIconFilterChip(
isSelected: Boolean,
displayText: String,
modifier: Modifier = Modifier,
+ chipShape: Shape = CircleShape,
onClick: () -> Unit = {},
) {
Row(
modifier = modifier
.background(
- shape = CircleShape,
+ shape = chipShape,
color = if (isSelected) NekiTheme.colorScheme.gray800
else NekiTheme.colorScheme.gray50,
)
@@ -100,12 +105,13 @@ private fun DefaultFilterChip(
isSelected: Boolean,
displayText: String,
modifier: Modifier = Modifier,
+ chipShape: Shape = CircleShape,
onClick: () -> Unit = {},
) {
Text(
modifier = modifier
.background(
- shape = CircleShape,
+ shape = chipShape,
color = if (isSelected) NekiTheme.colorScheme.gray800
else NekiTheme.colorScheme.gray50,
)
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
index 661f1104e..42192cde4 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
@@ -1,6 +1,7 @@
package com.neki.android.core.ui.component
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -14,7 +15,7 @@ import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@Composable
-fun PhotoGridItemOverlay(
+fun GridItemOverlay(
modifier: Modifier = Modifier,
shape: Shape = RoundedCornerShape(8.dp),
) {
@@ -42,18 +43,35 @@ fun SelectedPhotoGridItemOverlay(
shape: Shape = RoundedCornerShape(8.dp),
) {
Box(
- modifier = modifier.background(
- color = Color.Black.copy(alpha = 0.2f),
- shape = shape,
- ),
+ modifier = modifier
+ .border(
+ width = 2.dp,
+ color = NekiTheme.colorScheme.primary400,
+ shape = shape,
+ )
+ .background(
+ color = Color.Black.copy(alpha = 0.2f),
+ shape = shape,
+ ),
)
}
@ComponentPreview
@Composable
-private fun PhotoGridItemOverlayPreview() {
+private fun GridItemOverlayPreview() {
+ NekiTheme {
+ GridItemOverlay(
+ modifier = Modifier
+ .size(80.dp),
+ )
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun SelectedPhotoGridItemOverlayPreview() {
NekiTheme {
- PhotoGridItemOverlay(
+ SelectedPhotoGridItemOverlay(
modifier = Modifier
.size(80.dp),
)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
index 7021384ba..c0a0b91c2 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
@@ -38,7 +38,7 @@ import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.archive.impl.album_detail.component.AlbumDetailTopBar
import com.neki.android.feature.archive.impl.component.DeletePhotoDialog
-import com.neki.android.feature.archive.impl.component.EmptyContent
+import com.neki.android.feature.archive.impl.component.EmptyAlbumContent
import com.neki.android.feature.archive.impl.component.SelectablePhotoItem
import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_GRID_ITEM_SPACING
import com.neki.android.feature.archive.impl.const.ArchiveConst.PHOTO_GRAY_LAYOUT_BOTTOM_PADDING
@@ -109,7 +109,7 @@ internal fun AlbumDetailScreen(
}
if (isEmpty) {
- EmptyContent(
+ EmptyAlbumContent(
title = if (uiState.isFavoriteAlbum) "즐겨찾기" else uiState.title,
onClickBack = { onIntent(AlbumDetailIntent.ClickBackIcon) },
)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyAlbumContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyAlbumContent.kt
new file mode 100644
index 000000000..2bf38eab3
--- /dev/null
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyAlbumContent.kt
@@ -0,0 +1,55 @@
+package com.neki.android.feature.archive.impl.component
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.topbar.BackTitleTopBar
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.feature.archive.impl.main.component.EmptyContent
+
+@Composable
+internal fun EmptyAlbumContent(
+ title: String,
+ modifier: Modifier = Modifier,
+ onClickBack: () -> Unit = {},
+) {
+ Box(
+ modifier = modifier.fillMaxSize(),
+ ) {
+ EmptyTopBar(
+ title = title,
+ onClickBack = onClickBack,
+ )
+
+ EmptyContent(
+ modifier = Modifier.align(Alignment.Center),
+ emptyText = "아직 등록된 사진이 없어요\n찍은 네컷을 저장해보세요!",
+ )
+ }
+}
+
+@Composable
+private fun EmptyTopBar(
+ title: String,
+ onClickBack: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BackTitleTopBar(
+ modifier = modifier,
+ title = title,
+ onBack = onClickBack,
+ )
+}
+
+@ComponentPreview
+@Composable
+private fun EmptyAlbumContentPreview() {
+ NekiTheme {
+ EmptyAlbumContent(
+ title = "즐겨찾기",
+ )
+ }
+}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt
deleted file mode 100644
index 402073e14..000000000
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.neki.android.feature.archive.impl.component
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import com.neki.android.core.designsystem.ComponentPreview
-import com.neki.android.core.designsystem.R
-import com.neki.android.core.designsystem.topbar.BackTitleTopBar
-import com.neki.android.core.designsystem.ui.theme.NekiTheme
-
-private const val EMPTY_TEXT = "아직 등록된 사진이 없어요\n새로운 사진을 등록하고 앨범에 추가해보세요!"
-
-@Composable
-internal fun EmptyContent(
- title: String,
- modifier: Modifier = Modifier,
- emptyText: String = EMPTY_TEXT,
- onClickBack: () -> Unit = {},
-) {
- Box(
- modifier = modifier.fillMaxSize(),
- ) {
- EmptyTopBar(
- title = title,
- onClickBack = onClickBack,
- )
-
- Column(
- modifier = Modifier.align(Alignment.Center),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(16.dp),
- ) {
- Icon(
- imageVector = ImageVector.vectorResource(R.drawable.icon_empty_content),
- contentDescription = null,
- tint = Color.Unspecified,
- )
- Text(
- text = emptyText,
- style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.gray300,
- textAlign = TextAlign.Center,
- )
- }
- }
-}
-
-@Composable
-private fun EmptyTopBar(
- title: String,
- onClickBack: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- BackTitleTopBar(
- modifier = modifier,
- title = title,
- onBack = onClickBack,
- )
-}
-
-@ComponentPreview
-@Composable
-private fun EmptyContentPreview() {
- NekiTheme {
- EmptyContent(
- title = "즐겨찾기",
- )
- }
-}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
index 41e761506..cd6b4e0cb 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
@@ -1,6 +1,5 @@
package com.neki.android.feature.archive.impl.component
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -19,7 +18,7 @@ import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
import com.neki.android.core.ui.component.PhotoComponent
-import com.neki.android.core.ui.component.PhotoGridItemOverlay
+import com.neki.android.core.ui.component.GridItemOverlay
import com.neki.android.core.ui.component.SelectedPhotoGridItemOverlay
import com.neki.android.core.ui.component.SelectionCheckbox
@@ -37,14 +36,7 @@ internal fun SelectablePhotoItem(
) {
PhotoComponent(
photo = photo,
- modifier = Modifier.then(
- if (isSelected)
- Modifier.border(
- width = 2.dp,
- color = NekiTheme.colorScheme.primary400,
- shape = RoundedCornerShape(8.dp),
- ) else Modifier.clip(RoundedCornerShape(8.dp)),
- ),
+ modifier = Modifier.clip(RoundedCornerShape(8.dp)),
onClickItem = onClickItem,
)
@@ -54,7 +46,7 @@ internal fun SelectablePhotoItem(
shape = RoundedCornerShape(8.dp),
)
} else {
- PhotoGridItemOverlay(
+ GridItemOverlay(
modifier = Modifier.matchParentSize(),
shape = RoundedCornerShape(8.dp),
)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
index d3a8b095b..5b6785804 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
@@ -48,7 +48,7 @@ import com.neki.android.feature.archive.impl.main.component.ArchiveMainTopBar
import com.neki.android.feature.archive.impl.main.component.AlbumUploadOption
import com.neki.android.feature.archive.impl.main.component.SelectWithAlbumDialog
import com.neki.android.feature.archive.impl.main.component.GotoTopButton
-import com.neki.android.feature.archive.impl.main.component.NoPhotoContent
+import com.neki.android.feature.archive.impl.main.component.EmptyContent
import kotlinx.collections.immutable.persistentListOf
import timber.log.Timber
@@ -243,7 +243,12 @@ private fun ArchiveMainContent(
if (uiState.recentPhotos.isEmpty()) {
item(span = StaggeredGridItemSpan.FullLine) {
- NoPhotoContent()
+ EmptyContent(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 70.dp),
+ emptyText = "아직 등록된 사진이 없어요\n찍은 네컷을 저장해보세요!",
+ )
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
index 51cd6dc99..65faf8287 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
@@ -40,9 +40,12 @@ import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.modifier.backgroundHazeBlur
import com.neki.android.core.designsystem.modifier.cardShadow
+import com.neki.android.core.designsystem.modifier.dashStroke
import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.AlbumPreview
+import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_ALBUM_ITEM_HEIGHT
+import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_ALBUM_ITEM_WIDTH
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.rememberHazeState
@@ -153,6 +156,43 @@ internal fun ArchiveMainAlbumList(
}
}
+@Composable
+private fun AddAlbumItem(
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+) {
+ Box(
+ modifier = modifier
+ .height(ARCHIVE_ALBUM_ITEM_HEIGHT.dp)
+ .width(ARCHIVE_ALBUM_ITEM_WIDTH.dp)
+ .clip(RoundedCornerShape(8.dp))
+ .dashStroke(
+ color = NekiTheme.colorScheme.primary400,
+ strokeWidth = 2.dp,
+ cornerRadius = 8.dp,
+ )
+ .noRippleClickable(onClick = onClick),
+ ) {
+ Column(
+ modifier = Modifier.align(Alignment.Center),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_plus),
+ tint = NekiTheme.colorScheme.primary400,
+ contentDescription = null,
+ )
+ Text(
+ text = "새 앨범 추가",
+ style = NekiTheme.typography.body14Medium,
+ color = NekiTheme.colorScheme.primary400,
+ )
+ }
+ }
+}
+
@Composable
private fun ArchiveAlbumItem(
title: String,
@@ -166,23 +206,39 @@ private fun ArchiveAlbumItem(
Box(
modifier = modifier
- .height(166.dp)
+ .cardShadow(shape = RoundedCornerShape(8.dp))
+ .height(ARCHIVE_ALBUM_ITEM_HEIGHT.dp)
.clip(RoundedCornerShape(8.dp))
.noRippleClickable(onClick = onClick),
) {
- AsyncImage(
- modifier = Modifier
- .cardShadow(shape = RoundedCornerShape(8.dp))
- .matchParentSize()
- .hazeSource(hazeState)
- .then(
- if (!thumbnailImage.isNullOrBlank()) Modifier
- else Modifier.background(color = NekiTheme.colorScheme.gray50),
- ),
- model = thumbnailImage,
- contentDescription = null,
- contentScale = ContentScale.Crop,
- )
+ if (photoCount == 0 || thumbnailImage == null) {
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .background(NekiTheme.colorScheme.gray25)
+ .hazeSource(hazeState),
+ ) {
+ Icon(
+ modifier = Modifier
+ .padding(top = 38.dp)
+ .size(40.dp)
+ .align(Alignment.TopCenter),
+ imageVector = ImageVector.vectorResource(R.drawable.icon_empty_gallery_thumbnail),
+ tint = NekiTheme.colorScheme.gray100,
+ contentDescription = null,
+ )
+ }
+ } else {
+ AsyncImage(
+ modifier = Modifier
+ .background(color = Color.Black.copy(alpha = 0.04f))
+ .matchParentSize()
+ .hazeSource(hazeState),
+ model = thumbnailImage,
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ )
+ }
AlbumFolder(
modifier = Modifier.align(Alignment.BottomCenter),
hazeState = hazeState,
@@ -202,7 +258,7 @@ private fun AlbumFolder(
isFavorite: Boolean = false,
) {
AlbumFolderLayout(
- modifier = modifier.width(124.dp),
+ modifier = modifier.width(ARCHIVE_ALBUM_ITEM_WIDTH.dp),
hazeState = hazeState,
color = if (isFavorite) NekiTheme.colorScheme.favoriteAlbumCover
else NekiTheme.colorScheme.defaultAlbumCover,
@@ -282,12 +338,31 @@ private fun AlbumFolderLayout(
@ComponentPreview
@Composable
-private fun FavoriteAlbumItemPreview() {
+private fun AddAlbumItemPreview() {
NekiTheme {
Box(modifier = Modifier.padding(8.dp)) {
+ AddAlbumItem()
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun FavoriteAlbumItemPreview() {
+ NekiTheme {
+ Row(
+ modifier = Modifier.padding(8.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ArchiveAlbumItem(
+ isFavorite = true,
+ title = "즐겨찾기",
+ photoCount = 0,
+ )
ArchiveAlbumItem(
isFavorite = true,
title = "네키 화이팅화이팅",
+ thumbnailImage = "https://example.com/photo.jpg",
photoCount = 10,
)
}
@@ -298,9 +373,17 @@ private fun FavoriteAlbumItemPreview() {
@Composable
private fun ArchiveAlbumItemPreview() {
NekiTheme {
- Box(modifier = Modifier.padding(8.dp)) {
+ Row(
+ modifier = Modifier.padding(8.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ ArchiveAlbumItem(
+ title = "빈 앨범",
+ photoCount = 0,
+ )
ArchiveAlbumItem(
title = "네키 화이팅화이팅",
+ thumbnailImage = "https://example.com/photo.jpg",
photoCount = 10,
)
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
index 14b36cc0d..a341d713e 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
@@ -16,7 +16,7 @@ import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
import com.neki.android.core.ui.component.PhotoComponent
-import com.neki.android.core.ui.component.PhotoGridItemOverlay
+import com.neki.android.core.ui.component.GridItemOverlay
@Composable
internal fun ArchiveMainPhotoItem(
@@ -32,7 +32,7 @@ internal fun ArchiveMainPhotoItem(
onClickItem = onClickItem,
)
- PhotoGridItemOverlay(
+ GridItemOverlay(
modifier = Modifier.matchParentSize(),
shape = RoundedCornerShape(8.dp),
)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
index 443d40050..f87644bd2 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
@@ -29,6 +29,7 @@ import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.NekiIconButton
import com.neki.android.core.designsystem.logo.PrimaryNekiTypoLogo
import com.neki.android.core.designsystem.modifier.dropdownShadow
+import com.neki.android.core.designsystem.popup.ArrowDirection
import com.neki.android.core.designsystem.popup.ToolTipPopup
import com.neki.android.core.designsystem.topbar.NekiLeftTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -107,9 +108,10 @@ private fun ArchiveToolTip(
ToolTipPopup(
tooltipText = "버튼을 눌러 네컷을 추가할 수 있어요",
- color = NekiTheme.colorScheme.gray800,
offset = offset,
alignment = Alignment.TopEnd,
+ arrowDirection = ArrowDirection.Up,
+ arrowAlignment = Alignment.CenterEnd,
onDismissRequest = onDismissRequest,
)
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/EmptyContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/EmptyContent.kt
new file mode 100644
index 000000000..100d47921
--- /dev/null
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/EmptyContent.kt
@@ -0,0 +1,54 @@
+package com.neki.android.feature.archive.impl.main.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+internal fun EmptyContent(
+ emptyText: String,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Image(
+ modifier = Modifier
+ .width(148.dp)
+ .height(112.dp),
+ painter = painterResource(R.drawable.image_empty_photo),
+ contentDescription = null,
+ )
+ Text(
+ text = emptyText,
+ style = NekiTheme.typography.body14Medium,
+ color = NekiTheme.colorScheme.gray200,
+ textAlign = TextAlign.Center,
+ )
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun EmptyContentPreview() {
+ NekiTheme {
+ EmptyContent(
+ emptyText = "아직 등록된 사진이 없어요\n찍은 네컷을 네키에 저장해보세요!",
+ )
+ }
+}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/NoPhotoContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/NoPhotoContent.kt
deleted file mode 100644
index cfc049feb..000000000
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/NoPhotoContent.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.neki.android.feature.archive.impl.main.component
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import com.neki.android.core.designsystem.ui.theme.NekiTheme
-
-@Composable
-internal fun NoPhotoContent(
- modifier: Modifier = Modifier,
-) {
- Column(
- modifier = modifier
- .fillMaxWidth()
- .padding(vertical = 70.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(22.dp),
- ) {
- Box(
- modifier = Modifier
- .clip(CircleShape)
- .size(104.dp)
- .background(
- color = NekiTheme.colorScheme.gray50,
- shape = CircleShape,
- ),
- )
- Text(
- text = "아직 등록된 사진이 없어요\n찍은 네컷을 네키에 저장해보세요!",
- style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.gray300,
- textAlign = TextAlign.Center,
- )
- }
-}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
index 4282a8bd0..aa8c35d72 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
@@ -41,7 +41,7 @@ import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.archive.impl.component.DeletePhotoDialog
-import com.neki.android.feature.archive.impl.component.EmptyContent
+import com.neki.android.feature.archive.impl.component.EmptyAlbumContent
import com.neki.android.feature.archive.impl.component.SelectablePhotoItem
import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_GRID_ITEM_SPACING
import com.neki.android.feature.archive.impl.const.ArchiveConst.PHOTO_GRAY_LAYOUT_BOTTOM_PADDING
@@ -123,7 +123,7 @@ internal fun AllPhotoScreen(
}
if (isEmpty) {
- EmptyContent(
+ EmptyAlbumContent(
title = "모든 사진",
onClickBack = { onIntent(AllPhotoIntent.ClickTopBarBackIcon) },
)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoFilterBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoFilterBar.kt
index b7bded7f7..59ca2dd33 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoFilterBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoFilterBar.kt
@@ -1,31 +1,22 @@
package com.neki.android.feature.archive.impl.photo.component
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.RoundedCornerShape
-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.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Popup
-import androidx.compose.ui.window.PopupProperties
import com.neki.android.core.designsystem.ComponentPreview
-import com.neki.android.core.designsystem.modifier.clickableSingle
-import com.neki.android.core.designsystem.modifier.dropdownShadow
import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.core.ui.component.DropdownPopup
import com.neki.android.core.ui.component.FilterBar
import com.neki.android.feature.archive.impl.photo.PhotoFilter
+import kotlinx.collections.immutable.toImmutableList
@Composable
internal fun AllPhotoFilterBar(
@@ -51,59 +42,23 @@ internal fun AllPhotoFilterBar(
onClickDefaultChip = onClickFavoriteChip,
)
if (showFilterPopup) {
- PhotoFilterPopup(
- selectedFilter = selectedFilter,
+ val density = LocalDensity.current
+ val popupOffsetX = with(density) { 20.dp.toPx().toInt() }
+ val popupOffsetY = with(density) { 46.dp.toPx().toInt() }
+
+ DropdownPopup(
+ items = PhotoFilter.entries.toImmutableList(),
+ selectedItem = selectedFilter,
+ onSelect = onClickFilterRow,
onDismissRequest = onDismissPopup,
- onClickFilterRow = onClickFilterRow,
+ itemLabel = { it.label },
+ modifier = Modifier.width(96.dp),
+ offset = IntOffset(x = popupOffsetX, y = popupOffsetY),
)
}
}
}
-@Composable
-private fun PhotoFilterPopup(
- selectedFilter: PhotoFilter,
- onDismissRequest: () -> Unit,
- onClickFilterRow: (PhotoFilter) -> Unit,
-) {
- val density = LocalDensity.current
- val popupOffsetX = with(density) { 20.dp.toPx().toInt() }
- val popupOffsetY = with(density) { 46.dp.toPx().toInt() }
-
- Popup(
- offset = IntOffset(x = popupOffsetX, y = popupOffsetY),
- alignment = Alignment.TopStart,
- onDismissRequest = onDismissRequest,
- properties = PopupProperties(focusable = true),
- ) {
- Column(
- modifier = Modifier
- .dropdownShadow(shape = RoundedCornerShape(8.dp))
- .background(
- color = NekiTheme.colorScheme.white,
- shape = RoundedCornerShape(8.dp),
- )
- .width(96.dp)
- .padding(vertical = 6.dp),
- ) {
- PhotoFilter.entries.forEach { filter ->
- Text(
- modifier = Modifier
- .fillMaxWidth()
- .background(
- color = if (selectedFilter == filter) NekiTheme.colorScheme.gray50
- else NekiTheme.colorScheme.white,
- )
- .clickableSingle { onClickFilterRow(filter) }
- .padding(horizontal = 16.dp, vertical = 5.dp),
- text = filter.label,
- style = NekiTheme.typography.body14Medium,
- )
- }
- }
- }
-}
-
@ComponentPreview
@Composable
private fun AllPhotoFilterBarDefaultPreview() {
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/PhotoActionBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/PhotoActionBar.kt
index f63120a05..d085a7875 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/PhotoActionBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/PhotoActionBar.kt
@@ -3,12 +3,8 @@ package com.neki.android.feature.archive.impl.photo.component
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -18,6 +14,7 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.actionbar.NekiBothSidesActionBar
import com.neki.android.core.designsystem.modifier.noRippleClickableSingle
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -35,41 +32,37 @@ internal fun PhotoActionBar(
enter = expandVertically(expandFrom = Alignment.Top),
exit = shrinkVertically(shrinkTowards = Alignment.Top),
) {
- Column(
- modifier = modifier.fillMaxWidth(),
- ) {
- HorizontalDivider(
- modifier = Modifier.fillMaxWidth(),
- thickness = 1.dp,
- color = NekiTheme.colorScheme.gray75,
- )
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(20.dp),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
+ NekiBothSidesActionBar(
+ modifier = Modifier.fillMaxWidth(),
+ startContent = {
Icon(
- modifier = Modifier.noRippleClickableSingle(
- enabled = isEnabled,
- onClick = onClickDownload,
- ),
+ modifier = Modifier
+ .padding(20.dp)
+ .noRippleClickableSingle(
+ enabled = isEnabled,
+ onClick = onClickDownload,
+ ),
imageVector = ImageVector.vectorResource(R.drawable.icon_download),
contentDescription = null,
- tint = if (isEnabled) NekiTheme.colorScheme.gray600 else NekiTheme.colorScheme.gray200,
+ tint = if (isEnabled) NekiTheme.colorScheme.gray700
+ else NekiTheme.colorScheme.gray200,
)
+ },
+ endContent = {
Icon(
- modifier = Modifier.noRippleClickableSingle(
- enabled = isEnabled,
- onClick = onClickDelete,
- ),
+ modifier = Modifier
+ .padding(20.dp)
+ .noRippleClickableSingle(
+ enabled = isEnabled,
+ onClick = onClickDelete,
+ ),
imageVector = ImageVector.vectorResource(R.drawable.icon_trash),
contentDescription = null,
- tint = if (isEnabled) NekiTheme.colorScheme.gray600 else NekiTheme.colorScheme.gray200,
+ tint = if (isEnabled) NekiTheme.colorScheme.gray700
+ else NekiTheme.colorScheme.gray200,
)
- }
- }
+ },
+ )
}
}
@@ -90,7 +83,7 @@ private fun PhotoActionBarDisabledPreview() {
NekiTheme {
PhotoActionBar(
visible = true,
- isEnabled = true,
+ isEnabled = false,
)
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/PhotoDetailScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/PhotoDetailScreen.kt
index f3256d625..04bbf638e 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/PhotoDetailScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/PhotoDetailScreen.kt
@@ -4,22 +4,20 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.neki.android.core.designsystem.DevicePreview
import com.neki.android.core.designsystem.topbar.BackTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
-import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.navigation.result.LocalResultEventBus
+import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.archive.impl.component.DeletePhotoDialog
@@ -83,12 +81,6 @@ internal fun PhotoDetailScreen(
contentScale = ContentScale.Fit,
)
- HorizontalDivider(
- modifier = Modifier.fillMaxWidth(),
- thickness = 1.dp,
- color = NekiTheme.colorScheme.gray75,
- )
-
PhotoDetailActionBar(
isFavorite = uiState.photo.isFavorite,
onClickDownload = { onIntent(PhotoDetailIntent.ClickDownloadIcon) },
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/component/PhotoDetailActionBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/component/PhotoDetailActionBar.kt
index 4ed8f8da4..22d7759bf 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/component/PhotoDetailActionBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo_detail/component/PhotoDetailActionBar.kt
@@ -7,13 +7,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.actionbar.NekiBothSidesActionBar
import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -25,51 +25,48 @@ internal fun PhotoDetailActionBar(
onClickFavorite: () -> Unit = {},
onClickDelete: () -> Unit = {},
) {
- Row(
- modifier = modifier
- .fillMaxWidth()
- .padding(20.dp),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Row(
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- ) {
- Icon(
- modifier = Modifier
- .size(28.dp)
- .noRippleClickable { onClickDownload() },
- imageVector = ImageVector.vectorResource(R.drawable.icon_download),
- contentDescription = null,
- tint = NekiTheme.colorScheme.gray500,
- )
+ NekiBothSidesActionBar(
+ modifier = modifier.fillMaxWidth(),
+ startContent = {
+ Row(
+ modifier = Modifier.padding(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(28.dp)
+ .noRippleClickable { onClickDownload() },
+ imageVector = ImageVector.vectorResource(R.drawable.icon_download),
+ contentDescription = null,
+ tint = NekiTheme.colorScheme.gray700,
+ )
+ Icon(
+ modifier = Modifier
+ .size(28.dp)
+ .noRippleClickable { onClickFavorite() },
+ imageVector = ImageVector.vectorResource(
+ if (isFavorite) R.drawable.icon_heart_filled
+ else R.drawable.icon_heart_stroked,
+ ),
+ contentDescription = null,
+ tint = if (isFavorite) NekiTheme.colorScheme.primary400
+ else NekiTheme.colorScheme.gray700,
+ )
+ }
+ },
+ endContent = {
Icon(
modifier = Modifier
+ .padding(20.dp)
.size(28.dp)
- .noRippleClickable { onClickFavorite() },
- imageVector = ImageVector.vectorResource(
- if (isFavorite) R.drawable.icon_heart_filled
- else R.drawable.icon_heart_stroked,
- ),
+ .noRippleClickable { onClickDelete() },
+ imageVector = ImageVector.vectorResource(R.drawable.icon_trash),
contentDescription = null,
- tint = if (isFavorite) {
- NekiTheme.colorScheme.primary400
- } else {
- NekiTheme.colorScheme.gray700
- },
+ tint = NekiTheme.colorScheme.gray700,
)
- }
-
- Icon(
- modifier = Modifier
- .size(28.dp)
- .noRippleClickable { onClickDelete() },
- imageVector = ImageVector.vectorResource(R.drawable.icon_trash),
- contentDescription = null,
- tint = NekiTheme.colorScheme.gray600,
- )
- }
+ },
+ )
}
@ComponentPreview
diff --git a/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/const/MyPageConst.kt b/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/const/MyPageConst.kt
new file mode 100644
index 000000000..2306c71d6
--- /dev/null
+++ b/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/const/MyPageConst.kt
@@ -0,0 +1,5 @@
+package com.neki.android.feature.mypage.impl.const
+
+internal object MyPageConst {
+ internal const val NICKNAME_MAX_LENGTH = 12
+}
diff --git a/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/profile/EditProfileScreen.kt b/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/profile/EditProfileScreen.kt
index 326f6fa16..2288688eb 100644
--- a/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/profile/EditProfileScreen.kt
+++ b/feature/mypage/impl/src/main/java/com/neki/android/feature/mypage/impl/profile/EditProfileScreen.kt
@@ -1,21 +1,12 @@
package com.neki.android.feature.mypage.impl.profile
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.InputTransformation
-import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.maxLength
import androidx.compose.foundation.text.input.rememberTextFieldState
-import androidx.compose.material3.Text
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
@@ -28,11 +19,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.NekiTextField
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.ui.compose.collectWithLifecycle
@@ -48,6 +39,7 @@ import com.neki.android.feature.mypage.impl.profile.component.EditProfileImage
import com.neki.android.feature.mypage.impl.profile.component.ProfileEditTopBar
import com.neki.android.feature.mypage.impl.profile.component.SelectProfileImageDialog
import com.neki.android.feature.mypage.impl.profile.component.ProfileImageOption
+import com.neki.android.feature.mypage.impl.const.MyPageConst
import timber.log.Timber
@Composable
@@ -120,60 +112,16 @@ fun EditProfileScreen(
profileImage = displayProfileImage,
onClickCameraIcon = { onIntent(MyPageIntent.ClickCameraIcon) },
)
- Column(
+ NekiTextField(
+ textFieldState = textFieldState,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
- verticalArrangement = Arrangement.spacedBy(6.dp),
- ) {
- Text(
- text = "닉네임",
- style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.gray700,
- )
- BasicTextField(
- state = textFieldState,
- modifier = Modifier
- .fillMaxWidth()
- .background(
- color = NekiTheme.colorScheme.white,
- shape = RoundedCornerShape(8.dp),
- )
- .border(
- width = 1.dp,
- color = if (textFieldState.text.isEmpty()) NekiTheme.colorScheme.gray75 else NekiTheme.colorScheme.gray700,
- shape = RoundedCornerShape(8.dp),
- )
- .padding(horizontal = 16.dp, vertical = 13.dp),
- textStyle = NekiTheme.typography.body16Medium.copy(
- color = NekiTheme.colorScheme.gray900,
- ),
- inputTransformation = InputTransformation.maxLength(12),
- cursorBrush = SolidColor(NekiTheme.colorScheme.gray800),
- lineLimits = TextFieldLineLimits.SingleLine,
- decorator = { innerTextField ->
- Row(
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Box(modifier = Modifier.weight(1f)) {
- if (textFieldState.text.isEmpty()) {
- Text(
- text = "닉네임을 입력해주세요.",
- style = NekiTheme.typography.body16Regular,
- color = NekiTheme.colorScheme.gray300,
- )
- }
- innerTextField()
- }
- Text(
- text = "${textFieldState.text.length}/12",
- style = NekiTheme.typography.caption12Regular,
- color = NekiTheme.colorScheme.gray300,
- )
- }
- },
- )
- }
+ titleLabel = "닉네임",
+ placeholder = "닉네임을 입력해주세요.",
+ maxLength = MyPageConst.NICKNAME_MAX_LENGTH,
+ inputTransformation = InputTransformation.maxLength(MyPageConst.NICKNAME_MAX_LENGTH),
+ )
}
if (uiState.isShowImageSelectDialog) {
diff --git a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt
index 40c3b1513..ba83e8d8d 100644
--- a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt
+++ b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/component/QRScannerContent.kt
@@ -161,7 +161,7 @@ internal fun QRScannerContent(
NekiIconButton(
modifier = Modifier
.padding(top = 37.dp)
- .size(48.dp)
+ .size(56.dp)
.clip(CircleShape)
.background(
if (isTorchEnabled) Color.White
@@ -170,6 +170,7 @@ internal fun QRScannerContent(
onClick = { onIntent(QRScanIntent.ToggleTorch) },
) {
Icon(
+ modifier = Modifier.size(28.dp),
imageVector = ImageVector.vectorResource(
if (isTorchEnabled) R.drawable.icon_torch_on
else R.drawable.icon_torch_off,
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/PoseDetailScreen.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/PoseDetailScreen.kt
index 113c37264..f7a6f8d84 100644
--- a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/PoseDetailScreen.kt
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/PoseDetailScreen.kt
@@ -3,23 +3,18 @@ package com.neki.android.feature.pose.impl.detail
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
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.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.neki.android.core.designsystem.DevicePreview
-import com.neki.android.core.designsystem.button.NekiIconButton
import com.neki.android.core.designsystem.topbar.BackTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Pose
@@ -27,7 +22,7 @@ import com.neki.android.core.navigation.result.LocalResultEventBus
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.pose.api.PoseResult
-import com.neki.android.core.designsystem.R
+import com.neki.android.feature.pose.impl.detail.component.PoseActionBar
@Composable
internal fun PoseDetailRoute(
@@ -87,22 +82,10 @@ internal fun PoseDetailScreen(
thickness = 1.dp,
color = NekiTheme.colorScheme.gray75,
)
- NekiIconButton(
- modifier = Modifier
- .align(Alignment.End)
- .padding(8.dp),
- onClick = { onIntent(PoseDetailIntent.ClickScrapIcon) },
- ) {
- Icon(
- imageVector = ImageVector.vectorResource(
- if (uiState.pose.isScrapped) R.drawable.icon_scrap_filled
- else R.drawable.icon_scrap_stroked,
- ),
- contentDescription = null,
- tint = if (uiState.pose.isScrapped) NekiTheme.colorScheme.gray900
- else NekiTheme.colorScheme.gray500,
- )
- }
+ PoseActionBar(
+ isScrapped = uiState.pose.isScrapped,
+ onClickScrap = { onIntent(PoseDetailIntent.ClickScrapIcon) },
+ )
}
}
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/component/PoseActionBar.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/component/PoseActionBar.kt
new file mode 100644
index 000000000..9c0b73011
--- /dev/null
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/detail/component/PoseActionBar.kt
@@ -0,0 +1,59 @@
+package com.neki.android.feature.pose.impl.detail.component
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.actionbar.NekiEndActionBar
+import com.neki.android.core.designsystem.button.NekiIconButton
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+internal fun PoseActionBar(
+ isScrapped: Boolean,
+ modifier: Modifier = Modifier,
+ onClickScrap: () -> Unit = {},
+) {
+ NekiEndActionBar(
+ modifier = modifier.fillMaxWidth(),
+ ) {
+ NekiIconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = onClickScrap,
+ ) {
+ Icon(
+ modifier = Modifier.size(28.dp),
+ imageVector = ImageVector.vectorResource(
+ if (isScrapped) R.drawable.icon_scrap_filled
+ else R.drawable.icon_scrap_stroked,
+ ),
+ contentDescription = null,
+ tint = if (isScrapped) NekiTheme.colorScheme.gray900
+ else NekiTheme.colorScheme.gray500,
+ )
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun ScrappedPoseActionBarPreview() {
+ NekiTheme {
+ PoseActionBar(isScrapped = true)
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun UnScrappedPoseActionBarPreview() {
+ NekiTheme {
+ PoseActionBar(isScrapped = false)
+ }
+}
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/PoseListContent.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/PoseListContent.kt
index d12ddbe76..a7a605fad 100644
--- a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/PoseListContent.kt
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/PoseListContent.kt
@@ -21,7 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
@@ -33,9 +32,9 @@ import coil3.compose.AsyncImage
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.modifier.noRippleClickable
-import com.neki.android.core.designsystem.modifier.poseBackground
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Pose
+import com.neki.android.core.ui.component.GridItemOverlay
import com.neki.android.feature.pose.impl.const.PoseConst.POSE_LAYOUT_BOTTOM_PADDING
import com.neki.android.feature.pose.impl.const.PoseConst.POSE_LAYOUT_VERTICAL_SPACING
@@ -101,10 +100,9 @@ private fun PoseItem(
}
},
)
- Box(
- modifier = Modifier
- .matchParentSize()
- .poseBackground(shape = RectangleShape),
+ GridItemOverlay(
+ modifier = Modifier.matchParentSize(),
+ shape = RoundedCornerShape(12.dp),
)
if (pose.isScrapped) {
Icon(
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/RecommendationChip.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/RecommendationChip.kt
index 35c2c8cbe..728975cdf 100644
--- a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/RecommendationChip.kt
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/main/component/RecommendationChip.kt
@@ -17,6 +17,7 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.modifier.buttonShadow
import com.neki.android.core.designsystem.modifier.clickableSingle
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -27,6 +28,7 @@ internal fun RecommendationChip(
) {
Row(
modifier = modifier
+ .buttonShadow()
.clip(CircleShape)
.clickableSingle(onClick = onClick)
.background(shape = CircleShape, color = NekiTheme.colorScheme.gray800)
@@ -35,7 +37,7 @@ internal fun RecommendationChip(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
- modifier = Modifier.size(20.dp),
+ modifier = Modifier.size(24.dp),
imageVector = ImageVector.vectorResource(R.drawable.icon_repeat),
contentDescription = null,
tint = NekiTheme.colorScheme.primary400,
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseFloatingBar.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseFloatingBar.kt
index 824f44374..0b560a094 100644
--- a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseFloatingBar.kt
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseFloatingBar.kt
@@ -22,9 +22,9 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.NekiIconButton
import com.neki.android.core.designsystem.ui.theme.NekiTheme
-import com.neki.android.core.designsystem.R
@Composable
internal fun RandomPoseFloatingBarContent(
@@ -82,10 +82,6 @@ private fun RandomPoseFloatingBar(
modifier = modifier
.fillMaxWidth()
.clip(CircleShape)
- .background(
- color = NekiTheme.colorScheme.white.copy(alpha = 0.6f),
- shape = CircleShape,
- )
.border(
width = 1.dp,
brush = Brush.verticalGradient(
@@ -96,6 +92,10 @@ private fun RandomPoseFloatingBar(
),
shape = CircleShape,
)
+ .background(
+ color = NekiTheme.colorScheme.white.copy(alpha = 0.3f),
+ shape = CircleShape,
+ )
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,