Menyesuaikan UI/UX Design pada StarredNotesScreen.kt dan TopBar pada seluruh komponen Halaman

This commit is contained in:
202310715297 RAIHAN ARIQ MUZAKKI 2025-12-18 16:58:33 +07:00
parent b264f87b14
commit 7a67943800
5 changed files with 181 additions and 133 deletions

View File

@ -199,13 +199,9 @@ fun NotesApp() {
"trash" -> "Sampah"
else -> "AI Notes"
},
showBackButton = (selectedCategory != null && currentScreen == "main") || currentScreen == "starred",
showBackButton = (selectedCategory != null && currentScreen == "main"),
onBackClick = {
if (currentScreen == "starred") {
currentScreen = "main"
} else {
selectedCategory = null
}
selectedCategory = null
},
onMenuClick = { drawerState = !drawerState },
onSearchClick = { showSearch = !showSearch },
@ -354,6 +350,16 @@ fun NotesApp() {
it
}
}
},
onNoteEdit = { note ->
editingNote = note
showNoteDialog = true
},
onNoteDelete = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isDeleted = true)
else it
}
}
)
@ -392,8 +398,6 @@ fun NotesApp() {
fullScreenNote = note
showFullScreenNote = true
},
onMenuClick = { drawerState = true },
onBack = { currentScreen = "main" },
onUnpin = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isPinned = false)

View File

@ -4,6 +4,7 @@ import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
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.automirrored.filled.ArrowBack
@ -14,6 +15,7 @@ import androidx.compose.material3.*
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.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
@ -34,113 +36,141 @@ fun ModernTopBar(
onSearchQueryChange: (String) -> Unit,
showSearch: Boolean
) {
// 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 = 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 atau kategori...",
color = AppColors.OnSurfaceVariant,
fontSize = 15.sp
)
},
colors = TextFieldDefaults.colors(
focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = AppColors.SurfaceVariant,
focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = AppColors.OnSurface,
cursorColor = AppColors.Primary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
// Floating Top Bar with same style as Bottom Bar
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Surface(
modifier = Modifier
.fillMaxWidth()
.shadow(
elevation = Constants.Elevation.Large.dp,
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
),
color = AppColors.SurfaceElevated,
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
) {
// 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
Row(
modifier = Modifier
.weight(1f)
.heightIn(min = 48.dp),
shape = RoundedCornerShape(Constants.Radius.Medium.dp),
singleLine = true,
textStyle = LocalTextStyle.current.copy(fontSize = 15.sp)
)
Spacer(modifier = Modifier.width(Constants.Spacing.Small.dp))
// Close Search Button
IconButton(
onClick = {
onSearchQueryChange("")
onSearchClick()
}
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
Icons.Default.Close,
contentDescription = "Close Search",
tint = AppColors.OnSurfaceVariant
// Search TextField
OutlinedTextField(
value = searchQuery,
onValueChange = onSearchQueryChange,
placeholder = {
Text(
"Cari...",
color = AppColors.OnSurfaceVariant,
fontSize = 15.sp
)
},
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = AppColors.SurfaceVariant,
focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = AppColors.OnSurface,
cursorColor = AppColors.Primary,
focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent
),
modifier = Modifier
.weight(1f)
.heightIn(min = 48.dp),
shape = RoundedCornerShape(Constants.Radius.Large.dp),
singleLine = true,
textStyle = LocalTextStyle.current.copy(fontSize = 15.sp)
)
}
}
}
} else {
// Normal Mode - Minimalist
Surface(
modifier = Modifier
.fillMaxWidth()
.shadow(Constants.Elevation.Small.dp),
color = 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 = if (showBackButton) "Back" else "Menu",
tint = AppColors.OnSurface
)
}
// Title
Text(
title,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
color = AppColors.OnBackground,
modifier = Modifier.weight(1f)
)
// Close Search Button
IconButton(
onClick = {
onSearchQueryChange("")
onSearchClick()
},
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(AppColors.SurfaceVariant)
) {
Icon(
Icons.Default.Close,
contentDescription = "Close Search",
tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp)
)
}
}
} else {
// Normal Mode
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
// Back/Menu Button
IconButton(
onClick = if (showBackButton) onBackClick else onMenuClick,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(
if (showBackButton) AppColors.SurfaceVariant
else AppColors.Primary.copy(alpha = 0.1f)
)
) {
Icon(
if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu,
contentDescription = if (showBackButton) "Back" else "Menu",
tint = if (showBackButton) AppColors.OnSurface else AppColors.Primary,
modifier = Modifier.size(20.dp)
)
}
// Search Button
IconButton(onClick = onSearchClick) {
Icon(
Icons.Default.Search,
contentDescription = "Search",
tint = AppColors.OnSurfaceVariant
// Title
Text(
title,
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
color = AppColors.OnBackground,
modifier = Modifier
.weight(1f)
.padding(horizontal = 12.dp)
)
// Search Button
IconButton(
onClick = onSearchClick,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(AppColors.SurfaceVariant)
) {
Icon(
Icons.Default.Search,
contentDescription = "Search",
tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp)
)
}
}
}
}

View File

@ -14,6 +14,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
@ -22,6 +23,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.notesai.config.APIKey
import com.example.notesai.data.local.DataStoreManager
import com.example.notesai.data.model.*
import com.example.notesai.util.Constants
@ -35,7 +37,6 @@ import com.example.notesai.presentation.screens.ai.components.ChatHistoryDrawer
import com.example.notesai.presentation.screens.ai.components.CompactStatItem
import com.example.notesai.presentation.screens.ai.components.SuggestionChip
import com.example.notesai.util.AppColors
import com.example.notesai.config.APIKey
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View File

@ -27,7 +27,9 @@ fun MainScreen(
onNoteClick: (Note) -> Unit,
onPinToggle: (Note) -> Unit,
onCategoryDelete: (Category) -> Unit,
onCategoryEdit: (Category, String, Long, Long) -> Unit
onCategoryEdit: (Category, String, Long, Long) -> Unit,
onNoteEdit: (Note) -> Unit = {},
onNoteDelete: (Note) -> Unit = {}
) {
Column(modifier = Modifier.fillMaxSize()) {
if (selectedCategory == null) {
@ -120,7 +122,9 @@ fun MainScreen(
NoteCard(
note = note,
onClick = { onNoteClick(note) },
onPinClick = { onPinToggle(note) }
onPinClick = { onPinToggle(note) },
onEdit = { onNoteEdit(note) },
onDelete = { onNoteDelete(note) }
)
}
}

View File

@ -1,13 +1,16 @@
package com.example.notesai.presentation.screens.starred
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.notesai.presentation.components.EmptyState
import com.example.notesai.presentation.screens.starred.components.StarredNoteCard
@ -20,31 +23,37 @@ fun StarredNotesScreen(
notes: List<Note>,
categories: List<Category>,
onNoteClick: (Note) -> Unit,
onMenuClick: () -> Unit,
onBack: () -> Unit,
onUnpin: (Note) -> Unit
) {
val starredNotes = notes.filter { it.isPinned && !it.isArchived && !it.isDeleted }
.sortedByDescending { it.timestamp }
if (starredNotes.isEmpty()) {
EmptyState(
icon = Icons.Default.Star,
message = "Belum ada catatan berbintang",
subtitle = "Catatan yang ditandai berbintang akan muncul di sini"
)
} else {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(starredNotes) { note ->
val category = categories.find { it.id == note.categoryId }
StarredNoteCard(
note = note,
categoryName = category?.name ?: "Unknown",
onClick = { onNoteClick(note) },
onUnpin = { onUnpin(note) }
)
Column(modifier = Modifier.fillMaxSize()) {
if (starredNotes.isEmpty()) {
EmptyState(
icon = Icons.Default.Star,
message = "Belum ada catatan berbintang",
subtitle = "Catatan yang ditandai berbintang akan muncul di sini"
)
} else {
LazyColumn(
contentPadding = PaddingValues(
start = 16.dp,
end = 16.dp,
top = 16.dp,
bottom = 100.dp // Extra space untuk bottom bar
),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(starredNotes) { note ->
val category = categories.find { it.id == note.categoryId }
StarredNoteCard(
note = note,
categoryName = category?.name ?: "Unknown",
onClick = { onNoteClick(note) },
onUnpin = { onUnpin(note) }
)
}
}
}
}