Mengubah Warna dan menyesuaikan UI/UX Beranda
This commit is contained in:
parent
e541c4e234
commit
0f0ac6b8f3
@ -1,10 +1,11 @@
|
||||
package com.example.notesai
|
||||
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
@ -19,6 +20,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import java.util.UUID
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.example.notesai.data.local.DataStoreManager
|
||||
import com.example.notesai.presentation.components.DrawerMenu
|
||||
@ -37,20 +40,64 @@ import com.example.notesai.data.model.Category
|
||||
import com.example.notesai.util.updateWhere
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
// File: MainActivity.kt (Bagian Theme Setup)
|
||||
// Ganti MaterialTheme di setContent dengan ini:
|
||||
|
||||
import com.example.notesai.util.Constants
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
MaterialTheme(
|
||||
colorScheme = darkColorScheme(
|
||||
primary = Color(0xFF6366F1),
|
||||
secondary = Color(0xFFA855F7),
|
||||
background = Color(0xFF0F172A),
|
||||
surface = Color(0xFF1E293B),
|
||||
// Primary colors
|
||||
primary = Constants.AppColors.Primary,
|
||||
onPrimary = Color.White,
|
||||
primaryContainer = Constants.AppColors.PrimaryContainer,
|
||||
onPrimaryContainer = Color.White,
|
||||
|
||||
// Secondary colors
|
||||
secondary = Constants.AppColors.Secondary,
|
||||
onSecondary = Color.White,
|
||||
onBackground = Color(0xFFE2E8F0),
|
||||
onSurface = Color(0xFFE2E8F0)
|
||||
secondaryContainer = Constants.AppColors.SecondaryVariant,
|
||||
onSecondaryContainer = Color.White,
|
||||
|
||||
// Background colors
|
||||
background = Constants.AppColors.Background,
|
||||
onBackground = Constants.AppColors.OnBackground,
|
||||
|
||||
// Surface colors
|
||||
surface = Constants.AppColors.Surface,
|
||||
onSurface = Constants.AppColors.OnSurface,
|
||||
surfaceVariant = Constants.AppColors.SurfaceVariant,
|
||||
onSurfaceVariant = Constants.AppColors.OnSurfaceVariant,
|
||||
|
||||
// Error colors
|
||||
error = Constants.AppColors.Error,
|
||||
onError = Color.White,
|
||||
|
||||
// Other
|
||||
outline = Constants.AppColors.Border,
|
||||
outlineVariant = Constants.AppColors.Divider
|
||||
),
|
||||
typography = Typography(
|
||||
// Improve typography for better readability
|
||||
displayLarge = MaterialTheme.typography.displayLarge.copy(
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
headlineLarge = MaterialTheme.typography.headlineLarge.copy(
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
titleLarge = MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.SemiBold
|
||||
),
|
||||
bodyLarge = MaterialTheme.typography.bodyLarge.copy(
|
||||
lineHeight = 24.sp
|
||||
),
|
||||
bodyMedium = MaterialTheme.typography.bodyMedium.copy(
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
)
|
||||
) {
|
||||
Surface(
|
||||
@ -161,7 +208,12 @@ fun NotesApp() {
|
||||
floatingActionButton = {
|
||||
AnimatedVisibility(
|
||||
visible = currentScreen == "main" && !showFullScreenNote,
|
||||
enter = scaleIn() + fadeIn(),
|
||||
enter = scaleIn(
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessLow
|
||||
)
|
||||
) + fadeIn(),
|
||||
exit = scaleOut() + fadeOut()
|
||||
) {
|
||||
FloatingActionButton(
|
||||
@ -173,20 +225,18 @@ fun NotesApp() {
|
||||
showCategoryDialog = true
|
||||
}
|
||||
},
|
||||
containerColor = Color.Transparent,
|
||||
modifier = Modifier
|
||||
.shadow(8.dp, CircleShape)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7))
|
||||
containerColor = Constants.AppColors.Primary,
|
||||
contentColor = Color.White,
|
||||
elevation = FloatingActionButtonDefaults.elevation(
|
||||
defaultElevation = 8.dp,
|
||||
pressedElevation = 12.dp
|
||||
),
|
||||
shape = CircleShape
|
||||
)
|
||||
modifier = Modifier.size(64.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = if (selectedCategory != null) "Tambah Catatan" else "Tambah Kategori",
|
||||
tint = Color.White
|
||||
modifier = Modifier.size(28.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,43 +1,29 @@
|
||||
package com.example.notesai.presentation.components
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
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.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Archive
|
||||
import androidx.compose.material.icons.filled.Create
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.notesai.util.Constants
|
||||
|
||||
@Composable
|
||||
fun DrawerMenu(
|
||||
@ -45,133 +31,233 @@ fun DrawerMenu(
|
||||
onDismiss: () -> Unit,
|
||||
onItemClick: (String) -> Unit
|
||||
) {
|
||||
// Backdrop with blur effect
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.statusBarsPadding() // Padding untuk status bar
|
||||
.background(Color.Black.copy(alpha = 0.5f))
|
||||
.background(Constants.AppColors.Overlay)
|
||||
.clickable(
|
||||
onClick = onDismiss,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
) {
|
||||
Card(
|
||||
// Drawer Content
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(250.dp)
|
||||
.width(280.dp)
|
||||
.align(Alignment.CenterStart)
|
||||
.clickable(
|
||||
onClick = {},
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
),
|
||||
shape = RoundedCornerShape(topEnd = 0.dp, bottomEnd = 0.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = Color(0xFF1E293B)
|
||||
),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 16.dp)
|
||||
color = Constants.AppColors.Surface,
|
||||
shadowElevation = Constants.Elevation.ExtraLarge.dp
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
// Header Drawer dengan tombol close
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
// Header - Minimalist
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7))
|
||||
colors = listOf(
|
||||
Constants.AppColors.Primary.copy(alpha = 0.15f),
|
||||
Color.Transparent
|
||||
)
|
||||
)
|
||||
.padding(24.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
)
|
||||
.padding(Constants.Spacing.ExtraLarge.dp)
|
||||
) {
|
||||
Column {
|
||||
// App Icon with subtle background
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.background(
|
||||
color = Constants.AppColors.Primary.copy(alpha = 0.2f),
|
||||
shape = RoundedCornerShape(Constants.Radius.Medium.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Create,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(36.dp)
|
||||
tint = Constants.AppColors.Primary,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp))
|
||||
|
||||
Text(
|
||||
"AI Notes",
|
||||
Constants.APP_NAME,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
color = Constants.AppColors.OnBackground,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(Constants.Spacing.ExtraSmall.dp))
|
||||
|
||||
Text(
|
||||
"Smart & Modern",
|
||||
"Smart Note Taking",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = Color.White.copy(0.8f)
|
||||
color = Constants.AppColors.OnSurfaceVariant,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
|
||||
// // Tombol Close
|
||||
// IconButton(
|
||||
// onClick = onDismiss,
|
||||
// modifier = Modifier
|
||||
// .size(40.dp)
|
||||
// .background(
|
||||
// Color.White.copy(alpha = 0.2f),
|
||||
// shape = CircleShape
|
||||
// )
|
||||
// ) {
|
||||
// Icon(
|
||||
// Icons.Default.Close,
|
||||
// contentDescription = "Tutup Menu",
|
||||
// tint = Color.White,
|
||||
// modifier = Modifier.size(24.dp)
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(Constants.Spacing.Large.dp))
|
||||
|
||||
// Menu Items
|
||||
MenuItem(
|
||||
icon = Icons.Default.Home,
|
||||
DrawerMenuItem(
|
||||
icon = if (currentScreen == "main") Icons.Filled.Home else Icons.Outlined.Home,
|
||||
text = "Beranda",
|
||||
isSelected = currentScreen == "main"
|
||||
) { onItemClick("main") }
|
||||
isSelected = currentScreen == "main",
|
||||
onClick = { onItemClick("main") }
|
||||
)
|
||||
|
||||
MenuItem(
|
||||
icon = Icons.Default.Star,
|
||||
DrawerMenuItem(
|
||||
icon = if (currentScreen == "starred") Icons.Filled.Star else Icons.Outlined.StarBorder,
|
||||
text = "Berbintang",
|
||||
isSelected = currentScreen == "starred"
|
||||
) { onItemClick("starred") }
|
||||
isSelected = currentScreen == "starred",
|
||||
onClick = { onItemClick("starred") }
|
||||
)
|
||||
|
||||
MenuItem(
|
||||
icon = Icons.Default.Archive,
|
||||
DrawerMenuItem(
|
||||
icon = if (currentScreen == "archive") Icons.Filled.Archive else Icons.Outlined.Archive,
|
||||
text = "Arsip",
|
||||
isSelected = currentScreen == "archive"
|
||||
) { onItemClick("archive") }
|
||||
isSelected = currentScreen == "archive",
|
||||
onClick = { onItemClick("archive") }
|
||||
)
|
||||
|
||||
MenuItem(
|
||||
icon = Icons.Default.Delete,
|
||||
DrawerMenuItem(
|
||||
icon = if (currentScreen == "trash") Icons.Filled.Delete else Icons.Outlined.Delete,
|
||||
text = "Sampah",
|
||||
isSelected = currentScreen == "trash"
|
||||
) { onItemClick("trash") }
|
||||
isSelected = currentScreen == "trash",
|
||||
onClick = { onItemClick("trash") }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Footer
|
||||
Divider(
|
||||
color = Color.White.copy(alpha = 0.1f),
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
// Footer - Version info
|
||||
HorizontalDivider(
|
||||
color = Constants.AppColors.Divider,
|
||||
modifier = Modifier.padding(horizontal = Constants.Spacing.Medium.dp)
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(Constants.Spacing.Medium.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Version 1.0.0",
|
||||
"Version ${Constants.APP_VERSION}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.White.copy(alpha = 0.5f),
|
||||
modifier = Modifier.padding(16.dp)
|
||||
color = Constants.AppColors.OnSurfaceTertiary,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
|
||||
// Powered by badge
|
||||
Surface(
|
||||
color = Constants.AppColors.Primary.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(Constants.Radius.Small.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = Constants.Spacing.Small.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.AutoAwesome,
|
||||
contentDescription = null,
|
||||
tint = Constants.AppColors.Primary,
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
Text(
|
||||
"AI",
|
||||
color = Constants.AppColors.Primary,
|
||||
fontSize = 11.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DrawerMenuItem(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
// Scale animation
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (isSelected) 1.02f else 1f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessMedium
|
||||
),
|
||||
label = "scale"
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.scale(scale)
|
||||
.clip(RoundedCornerShape(Constants.Radius.Medium.dp))
|
||||
.clickable(onClick = onClick)
|
||||
.background(
|
||||
color = if (isSelected)
|
||||
Constants.AppColors.Primary.copy(alpha = 0.1f)
|
||||
else
|
||||
Color.Transparent
|
||||
)
|
||||
.padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Medium.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Icon with background
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = if (isSelected)
|
||||
Constants.AppColors.Primary.copy(alpha = 0.2f)
|
||||
else
|
||||
Constants.AppColors.SurfaceVariant,
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = null,
|
||||
tint = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(Constants.Spacing.Medium.dp))
|
||||
|
||||
// Text
|
||||
Text(
|
||||
text,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurface,
|
||||
fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal,
|
||||
fontSize = 15.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,31 +1,32 @@
|
||||
package com.example.notesai.presentation.components
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AutoAwesome
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.material.icons.outlined.AutoAwesome
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.outlined.StarBorder
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.notesai.util.Constants
|
||||
|
||||
@Composable
|
||||
fun ModernBottomBar(
|
||||
@ -33,70 +34,125 @@ fun ModernBottomBar(
|
||||
onHomeClick: () -> Unit,
|
||||
onAIClick: () -> Unit
|
||||
) {
|
||||
BottomAppBar(
|
||||
containerColor = Color.Transparent,
|
||||
// Floating Bottom Bar with Glassmorphism
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.shadow(8.dp, RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp))
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF1E293B).copy(0.95f),
|
||||
Color(0xFF334155).copy(0.95f)
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.shadow(
|
||||
elevation = Constants.Elevation.Large.dp,
|
||||
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
|
||||
),
|
||||
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)
|
||||
)
|
||||
color = Constants.AppColors.SurfaceElevated,
|
||||
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.padding(vertical = 8.dp, horizontal = 24.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable(onClick = onHomeClick)
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Home,
|
||||
contentDescription = "Home",
|
||||
tint = if (currentScreen == "main") Color(0xFF6366F1) else Color(0xFF94A3B8),
|
||||
modifier = Modifier.size(24.dp)
|
||||
// Home Button
|
||||
BottomBarItem(
|
||||
selected = currentScreen == "main",
|
||||
onClick = onHomeClick,
|
||||
icon = if (currentScreen == "main") Icons.Filled.Home else Icons.Outlined.Home,
|
||||
label = "Beranda"
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
"Home",
|
||||
color = if (currentScreen == "main") Color(0xFF6366F1) else Color(0xFF94A3B8),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = if (currentScreen == "main") FontWeight.Bold else FontWeight.Normal
|
||||
|
||||
// AI Button
|
||||
BottomBarItem(
|
||||
selected = currentScreen == "ai",
|
||||
onClick = onAIClick,
|
||||
icon = if (currentScreen == "ai") Icons.Filled.AutoAwesome else Icons.Outlined.AutoAwesome,
|
||||
label = "AI Helper"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBarItem(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
label: String
|
||||
) {
|
||||
// Scale animation
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (selected) 1.1f else 1f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessLow
|
||||
),
|
||||
label = "scale"
|
||||
)
|
||||
|
||||
// Color animation
|
||||
val iconColor by animateColorAsState(
|
||||
targetValue = if (selected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant,
|
||||
animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM),
|
||||
label = "color"
|
||||
)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable(onClick = onAIClick)
|
||||
.padding(vertical = 8.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
.padding(horizontal = 24.dp, vertical = 8.dp)
|
||||
) {
|
||||
// Icon with background indicator
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.scale(scale)
|
||||
) {
|
||||
// Background indicator
|
||||
if (selected) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = Constants.AppColors.Primary.copy(alpha = 0.15f),
|
||||
shape = CircleShape
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
Icons.Default.Star,
|
||||
contentDescription = "AI Helper",
|
||||
tint = if (currentScreen == "ai") Color(0xFFFBBF24) else Color(0xFF94A3B8),
|
||||
icon,
|
||||
contentDescription = label,
|
||||
tint = iconColor,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// Label with fade animation
|
||||
AnimatedVisibility(
|
||||
visible = selected,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
"AI Helper",
|
||||
color = if (currentScreen == "ai") Color(0xFFFBBF24) else Color(0xFF94A3B8),
|
||||
label,
|
||||
color = Constants.AppColors.Primary,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = if (currentScreen == "ai") FontWeight.Bold else FontWeight.Normal
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +1,25 @@
|
||||
package com.example.notesai.presentation.components
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.notesai.util.Constants
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@ -50,58 +33,116 @@ fun ModernTopBar(
|
||||
onSearchQueryChange: (String) -> Unit,
|
||||
showSearch: Boolean
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
if (showSearch) {
|
||||
// Smooth transition for search bar
|
||||
AnimatedContent(
|
||||
targetState = showSearch,
|
||||
transitionSpec = {
|
||||
fadeIn(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) togetherWith
|
||||
fadeOut(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM))
|
||||
},
|
||||
label = "topbar"
|
||||
) { isSearching ->
|
||||
if (isSearching) {
|
||||
// Search Mode - Minimalist
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.shadow(Constants.Elevation.Small.dp),
|
||||
color = Constants.AppColors.Surface
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Small.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Search TextField
|
||||
TextField(
|
||||
value = searchQuery,
|
||||
onValueChange = onSearchQueryChange,
|
||||
placeholder = { Text("Cari catatan...", color = Color.White.copy(0.6f)) },
|
||||
placeholder = {
|
||||
Text(
|
||||
"Cari catatan atau kategori...",
|
||||
color = Constants.AppColors.OnSurfaceVariant,
|
||||
fontSize = 15.sp
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
focusedTextColor = Color.White,
|
||||
unfocusedTextColor = Color.White,
|
||||
cursorColor = Color.White,
|
||||
focusedContainerColor = Constants.AppColors.SurfaceVariant,
|
||||
unfocusedContainerColor = Constants.AppColors.SurfaceVariant,
|
||||
focusedTextColor = Constants.AppColors.OnBackground,
|
||||
unfocusedTextColor = Constants.AppColors.OnSurface,
|
||||
cursorColor = Constants.AppColors.Primary,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.heightIn(min = 48.dp),
|
||||
shape = RoundedCornerShape(Constants.Radius.Medium.dp),
|
||||
singleLine = true,
|
||||
textStyle = LocalTextStyle.current.copy(fontSize = 15.sp)
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
title,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 22.sp
|
||||
|
||||
Spacer(modifier = Modifier.width(Constants.Spacing.Small.dp))
|
||||
|
||||
// Close Search Button
|
||||
IconButton(
|
||||
onClick = {
|
||||
onSearchQueryChange("")
|
||||
onSearchClick()
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Close,
|
||||
contentDescription = "Close Search",
|
||||
tint = Constants.AppColors.OnSurfaceVariant
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal Mode - Minimalist
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.shadow(Constants.Elevation.Small.dp),
|
||||
color = Constants.AppColors.Surface
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = Constants.Spacing.Small.dp, vertical = Constants.Spacing.Small.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Back/Menu Button
|
||||
IconButton(onClick = if (showBackButton) onBackClick else onMenuClick) {
|
||||
Icon(
|
||||
if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu,
|
||||
contentDescription = null,
|
||||
tint = Color.White
|
||||
contentDescription = if (showBackButton) "Back" else "Menu",
|
||||
tint = Constants.AppColors.OnSurface
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
|
||||
// Title
|
||||
Text(
|
||||
title,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 20.sp,
|
||||
color = Constants.AppColors.OnBackground,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
// Search Button
|
||||
IconButton(onClick = onSearchClick) {
|
||||
Icon(
|
||||
if (showSearch) Icons.Default.Close else Icons.Default.Search,
|
||||
Icons.Default.Search,
|
||||
contentDescription = "Search",
|
||||
tint = Color.White
|
||||
tint = Constants.AppColors.OnSurfaceVariant
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Transparent
|
||||
),
|
||||
modifier = Modifier
|
||||
.background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
// File: presentation/screens/main/MainScreen.kt
|
||||
package com.example.notesai.presentation.screens.main
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
@ -28,7 +27,7 @@ fun MainScreen(
|
||||
onNoteClick: (Note) -> Unit,
|
||||
onPinToggle: (Note) -> Unit,
|
||||
onCategoryDelete: (Category) -> Unit,
|
||||
onCategoryEdit: (Category, String, Long, Long) -> Unit // Parameter baru
|
||||
onCategoryEdit: (Category, String, Long, Long) -> Unit
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
if (selectedCategory == null) {
|
||||
@ -58,7 +57,12 @@ fun MainScreen(
|
||||
} else {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(2),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
top = 16.dp,
|
||||
bottom = 100.dp // Extra space untuk floating bottom bar
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalItemSpacing = 12.dp,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
@ -102,7 +106,12 @@ fun MainScreen(
|
||||
} else {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(2),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
top = 16.dp,
|
||||
bottom = 100.dp // Extra space untuk floating bottom bar
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalItemSpacing = 12.dp,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
// File: util/Constants.kt
|
||||
package com.example.notesai.util
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
object Constants {
|
||||
// App Info
|
||||
const val APP_NAME = "AI Notes"
|
||||
const val APP_NAME = "NotesAI"
|
||||
const val APP_VERSION = "1.0.0"
|
||||
|
||||
// DataStore
|
||||
@ -17,37 +16,88 @@ object Constants {
|
||||
const val MAX_CHAT_PREVIEW_LINES = 2
|
||||
const val GRID_COLUMNS = 2
|
||||
|
||||
// Gradients
|
||||
val GRADIENT_PRESETS = listOf(
|
||||
Pair(0xFF6366F1L, 0xFFA855F7L),
|
||||
Pair(0xFFEC4899L, 0xFFF59E0BL),
|
||||
Pair(0xFF8B5CF6L, 0xFFEC4899L),
|
||||
Pair(0xFF06B6D4L, 0xFF3B82F6L),
|
||||
Pair(0xFF10B981L, 0xFF059669L),
|
||||
Pair(0xFFF59E0BL, 0xFFEF4444L),
|
||||
Pair(0xFF6366F1L, 0xFF8B5CF6L),
|
||||
Pair(0xFFEF4444L, 0xFFDC2626L)
|
||||
)
|
||||
|
||||
// Colors
|
||||
// NEW MINIMALIST COLOR PALETTE
|
||||
object AppColors {
|
||||
val Primary = Color(0xFF6366F1)
|
||||
val Secondary = Color(0xFFA855F7)
|
||||
val Background = Color(0xFF0F172A)
|
||||
val Surface = Color(0xFF1E293B)
|
||||
val SurfaceVariant = Color(0xFF334155)
|
||||
val OnBackground = Color(0xFFE2E8F0)
|
||||
val OnSurface = Color(0xFFE2E8F0)
|
||||
val Success = Color(0xFF10B981)
|
||||
val Error = Color(0xFFEF4444)
|
||||
val Warning = Color(0xFFFBBF24)
|
||||
val TextSecondary = Color(0xFF94A3B8)
|
||||
val TextTertiary = Color(0xFF64748B)
|
||||
val Divider = Color(0xFF334155)
|
||||
// Backgrounds - Neutral Dark
|
||||
val Background = Color(0xFF0A0A0A) // Almost black
|
||||
val Surface = Color(0xFF141414) // Dark gray
|
||||
val SurfaceVariant = Color(0xFF1E1E1E) // Lighter dark gray
|
||||
val SurfaceElevated = Color(0xFF252525) // Elevated surface
|
||||
|
||||
// Primary Accent - Subtle Blue (minimalist)
|
||||
val Primary = Color(0xFF3B82F6) // Modern blue
|
||||
val PrimaryVariant = Color(0xFF60A5FA) // Light blue
|
||||
val PrimaryContainer = Color(0xFF1E3A8A) // Dark blue container
|
||||
|
||||
// Secondary Accent - Minimal use
|
||||
val Secondary = Color(0xFF8B5CF6) // Subtle purple
|
||||
val SecondaryVariant = Color(0xFFA78BFA) // Light purple
|
||||
|
||||
// Text Colors - High contrast for readability
|
||||
val OnBackground = Color(0xFFFFFFFF) // Pure white
|
||||
val OnSurface = Color(0xFFE5E5E5) // Off white
|
||||
val OnSurfaceVariant = Color(0xFF9CA3AF) // Gray text
|
||||
val OnSurfaceTertiary = Color(0xFF6B7280) // Muted gray
|
||||
|
||||
// Functional Colors
|
||||
val Success = Color(0xFF10B981) // Green
|
||||
val Error = Color(0xFFEF4444) // Red
|
||||
val Warning = Color(0xFFFBBF24) // Amber
|
||||
val Info = Color(0xFF3B82F6) // Blue
|
||||
|
||||
// Border & Dividers - Subtle
|
||||
val Border = Color(0xFF2A2A2A) // Subtle border
|
||||
val Divider = Color(0xFF1F1F1F) // Very subtle divider
|
||||
|
||||
// Overlay & Shadows
|
||||
val Overlay = Color(0xFF000000).copy(alpha = 0.5f)
|
||||
val Shadow = Color(0xFF000000).copy(alpha = 0.3f)
|
||||
|
||||
// Category Colors - Minimalist Palette (subtle & muted)
|
||||
val CategoryColors = listOf(
|
||||
Pair(0xFF3B82F6L, 0xFF60A5FAL), // Blue gradient
|
||||
Pair(0xFF8B5CF6L, 0xFFA78BFAL), // Purple gradient
|
||||
Pair(0xFF10B981L, 0xFF34D399L), // Green gradient
|
||||
Pair(0xFFF59E0BL, 0xFFFBBF24L), // Amber gradient
|
||||
Pair(0xFFEF4444L, 0xFFF87171L), // Red gradient
|
||||
Pair(0xFF06B6D4L, 0xFF22D3EEL), // Cyan gradient
|
||||
Pair(0xFFEC4899L, 0xFFF472B6L), // Pink gradient
|
||||
Pair(0xFF6366F1L, 0xFF818CF8L) // Indigo gradient
|
||||
)
|
||||
}
|
||||
|
||||
// Animation
|
||||
const val ANIMATION_DURATION = 300
|
||||
// Animation Durations
|
||||
const val ANIMATION_DURATION_SHORT = 150
|
||||
const val ANIMATION_DURATION_MEDIUM = 300
|
||||
const val ANIMATION_DURATION_LONG = 500
|
||||
const val FADE_IN_DURATION = 200
|
||||
const val FADE_OUT_DURATION = 200
|
||||
|
||||
// Spacing System (8dp grid)
|
||||
object Spacing {
|
||||
const val ExtraSmall = 4
|
||||
const val Small = 8
|
||||
const val Medium = 16
|
||||
const val Large = 24
|
||||
const val ExtraLarge = 32
|
||||
const val XXLarge = 48
|
||||
}
|
||||
|
||||
// Corner Radius
|
||||
object Radius {
|
||||
const val Small = 8
|
||||
const val Medium = 12
|
||||
const val Large = 16
|
||||
const val ExtraLarge = 20
|
||||
const val Round = 999
|
||||
}
|
||||
|
||||
// Elevation
|
||||
object Elevation {
|
||||
const val None = 0
|
||||
const val Small = 2
|
||||
const val Medium = 4
|
||||
const val Large = 8
|
||||
const val ExtraLarge = 16
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,9 @@ appcompat = "1.6.1"
|
||||
material = "1.10.0"
|
||||
activity = "1.8.0"
|
||||
constraintlayout = "2.1.4"
|
||||
uiText = "1.10.0"
|
||||
material3 = "1.4.0"
|
||||
animationCore = "1.10.0"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
@ -19,6 +22,9 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
androidx-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||
androidx-animation-core = { group = "androidx.compose.animation", name = "animation-core", version.ref = "animationCore" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user