Refine MsTextField component: introduce compact mode, enhance visual styling and error handling, and improve placeholder and keyboard interaction logic. Add Dimens and Colors updates, implement navigation rail and header layout for the desktop shell, and update ROADMAP documentation with planned phases.
This commit is contained in:
+8
-13
@@ -41,20 +41,16 @@ fun <T> MsSearchableSelect(
|
||||
|
||||
Column(modifier = modifier) {
|
||||
// --- 1. Das Anzeige-Feld (sieht aus wie ein TextField, öffnet aber den Dialog) ---
|
||||
OutlinedTextField(
|
||||
MsTextField(
|
||||
value = selectedOption?.let { optionLabel(it) } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text(label, style = MaterialTheme.typography.bodySmall) },
|
||||
placeholder = { Text(placeholder, style = MaterialTheme.typography.bodySmall) },
|
||||
trailingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = enabled) { showDialog = true },
|
||||
label = label,
|
||||
placeholder = placeholder,
|
||||
leadingIcon = Icons.Default.Search,
|
||||
modifier = modifier.clickable(enabled = enabled) { showDialog = true },
|
||||
enabled = enabled,
|
||||
singleLine = true,
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
shape = MaterialTheme.shapes.small
|
||||
)
|
||||
|
||||
// --- 2. Der Such-Dialog (Desktop-zentriert) ---
|
||||
@@ -75,17 +71,16 @@ fun <T> MsSearchableSelect(
|
||||
)
|
||||
|
||||
// Internes Suchfeld im Dialog
|
||||
OutlinedTextField(
|
||||
MsTextField(
|
||||
value = searchText,
|
||||
onValueChange = {
|
||||
searchText = it
|
||||
onSearchQueryChange(it)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = { Text("Suchbegriff eingeben...") },
|
||||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
|
||||
placeholder = "Suchbegriff eingeben...",
|
||||
leadingIcon = Icons.Default.Search,
|
||||
singleLine = true,
|
||||
shape = MaterialTheme.shapes.small
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
+46
-28
@@ -1,12 +1,11 @@
|
||||
package at.mocode.frontend.core.designsystem.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
@@ -14,6 +13,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import at.mocode.frontend.core.designsystem.theme.Dimens
|
||||
|
||||
@Composable
|
||||
fun MsTextField(
|
||||
@@ -31,28 +31,41 @@ fun MsTextField(
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = true,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
compact: Boolean = true // Desktop-optimiert (kompakter)
|
||||
) {
|
||||
val height = if (compact) Dimens.TextFieldHeight else Dimens.TextFieldHeightL
|
||||
|
||||
Column(modifier = modifier) {
|
||||
if (label != null) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(bottom = 4.dp, start = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = label?.let { { Text(it) } },
|
||||
placeholder = placeholder?.let { { Text(it) } },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = height),
|
||||
placeholder = placeholder?.let { { Text(it, style = MaterialTheme.typography.bodyMedium) } },
|
||||
leadingIcon = leadingIcon?.let { icon ->
|
||||
{ Icon(imageVector = icon, contentDescription = null) }
|
||||
{ Icon(imageVector = icon, contentDescription = null, modifier = Modifier.size(Dimens.IconSizeM)) }
|
||||
},
|
||||
trailingIcon = if (trailingIcon != null) {
|
||||
{
|
||||
IconButton(
|
||||
onClick = onTrailingIconClick ?: {}
|
||||
) {
|
||||
Icon(imageVector = trailingIcon, contentDescription = null)
|
||||
Icon(imageVector = trailingIcon, contentDescription = null, modifier = Modifier.size(Dimens.IconSizeM))
|
||||
}
|
||||
}
|
||||
} else null,
|
||||
@@ -61,6 +74,15 @@ fun MsTextField(
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
maxLines = maxLines,
|
||||
textStyle = MaterialTheme.typography.bodyMedium,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
|
||||
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction
|
||||
@@ -70,24 +92,20 @@ fun MsTextField(
|
||||
)
|
||||
|
||||
// Error or helper text
|
||||
when {
|
||||
isError && errorMessage != null -> {
|
||||
Text(
|
||||
text = errorMessage,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
helperText != null -> {
|
||||
Text(
|
||||
text = helperText,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
|
||||
)
|
||||
}
|
||||
if (isError && errorMessage != null) {
|
||||
Text(
|
||||
text = errorMessage,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 8.dp, top = 2.dp)
|
||||
)
|
||||
} else if (helperText != null) {
|
||||
Text(
|
||||
text = helperText,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 8.dp, top = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -14,12 +14,16 @@ object AppColors {
|
||||
val PrimaryContainer = Color(0xFFDEEBFF)
|
||||
val OnPrimaryContainer = Color(0xFF0052CC)
|
||||
|
||||
// Subtiles Sidebar-Grau / Navigation
|
||||
val NavigationSurface = Color(0xFFF4F5F7)
|
||||
val NavigationContent = Color(0xFF42526E)
|
||||
|
||||
// Helleres Blau für sekundäre Akzente
|
||||
val Secondary = Color(0xFF2684FF)
|
||||
val OnSecondary = Color.White
|
||||
|
||||
// Neutral- & Hintergrund (Light Mode)
|
||||
val BackgroundLight = Color(0xFFF4F5F7) // Helles Grau (nicht hartes Weiß)
|
||||
val BackgroundLight = Color(0xFFF9FAFB) // Sehr helles Grau für den Content Bereich
|
||||
val SurfaceLight = Color.White
|
||||
val OnBackgroundLight = Color(0xFF172B4D) // Fast Schwarz (besser lesbar)
|
||||
|
||||
|
||||
+13
@@ -13,10 +13,17 @@ object Dimens {
|
||||
val SpacingS = 8.dp // Standard Abstand zwischen Elementen
|
||||
val SpacingM = 16.dp // Abstand für Sektionen
|
||||
val SpacingL = 24.dp // Außenabstand für Screens
|
||||
val SpacingXL = 32.dp
|
||||
|
||||
// Navigations-Maße
|
||||
val NavRailWidth = 72.dp
|
||||
val NavRailExpandedWidth = 240.dp
|
||||
val TopBarHeight = 56.dp
|
||||
|
||||
// Sizes (Größen)
|
||||
val IconSizeS = 16.dp
|
||||
val IconSizeM = 24.dp
|
||||
val IconSizeL = 32.dp
|
||||
|
||||
// Borders
|
||||
val BorderThin = 1.dp
|
||||
@@ -24,4 +31,10 @@ object Dimens {
|
||||
// Corner Radius (Ecken)
|
||||
val CornerRadiusS = 4.dp // Leicht abgerundet (Enterprise Look)
|
||||
val CornerRadiusM = 8.dp
|
||||
val CornerRadiusL = 12.dp
|
||||
|
||||
// Form-Elemente (Eingabefelder, Buttons)
|
||||
val TextFieldHeight = 44.dp // Kompakte Höhe für Desktop-Enterprise-Apps
|
||||
val TextFieldHeightL = 56.dp // Standard Material Höhe (für prominente Felder)
|
||||
val ButtonHeight = 40.dp
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user