From 96c9abb264c0ea9488564f797c56d4c8eabfbca1 Mon Sep 17 00:00:00 2001 From: Stefan Mogeritsch Date: Tue, 31 Mar 2026 10:52:53 +0200 Subject: [PATCH] feat(design-system): add `MsActionToolbar` component and update roadmap - Introduced `MsActionToolbar` for consistent placement of primary actions like Save, Cancel, Add, and Delete. - Updated `Frontend_Komponenten_Roadmap.md` to mark this component as complete in Phase 4. Signed-off-by: Stefan Mogeritsch --- .../Frontend_Komponenten_Roadmap.md | 2 +- .../components/MsActionToolbar.kt | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsActionToolbar.kt diff --git a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md index f1a3d244..0de8595f 100644 --- a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md +++ b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md @@ -52,7 +52,7 @@ Eingabe von Stammdaten muss schnell und fehlerfrei erfolgen. Hier bringen wir alles zusammen, bevor das finale Routing implementiert wird. * [x] **`MsMasterDetailLayout`:** Standard-Layout für alle Stammdaten-Screens (Liste & Editor). -* [ ] **`MsActionToolbar`:** Einheitliche Platzierung von Hauptaktionen (Neu, Speichern, Drucken). +* [x] **`MsActionToolbar`:** Einheitliche Platzierung von Hauptaktionen (Neu, Speichern, Drucken). * [ ] **`MsDialogShell`:** Standardisierter Rahmen für Modale und Bestätigungsdialoge. --- diff --git a/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsActionToolbar.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsActionToolbar.kt new file mode 100644 index 00000000..acff2736 --- /dev/null +++ b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsActionToolbar.kt @@ -0,0 +1,102 @@ +package at.mocode.frontend.core.designsystem.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import at.mocode.frontend.core.designsystem.theme.Dimens + +/** + * Eine einheitliche Aktionsleiste für Editoren und Detail-Ansichten. + * + * @param onSave Callback für "Speichern". + * @param onCancel Callback für "Abbrechen". + * @param onDelete Callback für "Löschen" (Optional). + * @param onAdd Callback für "Neu" (Optional). + * @param extraActions Zusätzliche Aktionen (Optional, z.B. Drucken). + * @param isSaving Zeigt den Ladezustand beim Speichern an. + * @param canSave Steuert die Aktivierung des Speichern-Buttons. + */ +@Composable +fun MsActionToolbar( + onSave: () -> Unit, + onCancel: () -> Unit, + modifier: Modifier = Modifier, + onDelete: (() -> Unit)? = null, + onAdd: (() -> Unit)? = null, + extraActions: @Composable (RowScope.() -> Unit)? = null, + isSaving: Boolean = false, + canSave: Boolean = true, + title: String? = null +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + // --- 1. Titel-Bereich (Optional) --- + if (title != null) { + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface + ) + } else { + Spacer(Modifier.width(1.dp)) + } + + // --- 2. Button-Bereich --- + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(Dimens.SpacingS) + ) { + // Extra Aktionen (z.B. Drucken) + extraActions?.invoke(this) + + if (extraActions != null && (onAdd != null || onDelete != null)) { + VerticalDivider(modifier = Modifier.height(24.dp).padding(horizontal = 4.dp)) + } + + // Neu / Löschen + if (onAdd != null) { + MsButton( + text = "Neu", + onClick = onAdd, + variant = ButtonVariant.OUTLINE, + size = ButtonSize.SMALL + ) + } + + if (onDelete != null) { + MsButton( + text = "Löschen", + onClick = onDelete, + variant = ButtonVariant.TEXT, + size = ButtonSize.SMALL + ) + } + + VerticalDivider(modifier = Modifier.height(24.dp).padding(horizontal = 4.dp)) + + // Hauptaktionen: Abbrechen & Speichern + MsButton( + text = "Abbrechen", + onClick = onCancel, + variant = ButtonVariant.TEXT, + size = ButtonSize.SMALL + ) + + MsButton( + text = "Speichern", + onClick = onSave, + enabled = canSave, + isLoading = isSaving, + size = ButtonSize.SMALL + ) + } + } +}