From ad529f739510458185d3dad673a31c50cf30de43 Mon Sep 17 00:00:00 2001 From: Stefan Mogeritsch Date: Tue, 31 Mar 2026 10:52:25 +0200 Subject: [PATCH] feat(design-system): add `MsMasterDetailLayout` component and update roadmap - Introduced `MsMasterDetailLayout` as a standardized layout for master-detail screens, including support for customizable headers and split proportions. - Updated `Frontend_Komponenten_Roadmap.md` to mark this component as complete in Phase 4. Signed-off-by: Stefan Mogeritsch --- .../Frontend_Komponenten_Roadmap.md | 4 +- .../components/MsMasterDetailLayout.kt | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsMasterDetailLayout.kt diff --git a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md index 56d38e1f..f1a3d244 100644 --- a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md +++ b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md @@ -47,11 +47,11 @@ Eingabe von Stammdaten muss schnell und fehlerfrei erfolgen. * [x] **`MsValidationWrapper`:** Konsistente Anzeige von Fehlern und Warnungen (z.B. ÖTO-Validierungsregeln). * [x] **`MsSearchableSelect`:** Für die Verknüpfung von Reitern/Pferden (Autocomplete-Suche). -## Phase 4: Layout-Patterns & Navigation ⚪ [ZUKUNFT] +## Phase 4: Layout-Patterns & Navigation 🔵 [IN ARBEIT] Hier bringen wir alles zusammen, bevor das finale Routing implementiert wird. -* [ ] **`MsMasterDetailLayout`:** Standard-Layout für alle Stammdaten-Screens. +* [x] **`MsMasterDetailLayout`:** Standard-Layout für alle Stammdaten-Screens (Liste & Editor). * [ ] **`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/MsMasterDetailLayout.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsMasterDetailLayout.kt new file mode 100644 index 00000000..9c0e7c8b --- /dev/null +++ b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsMasterDetailLayout.kt @@ -0,0 +1,83 @@ +package at.mocode.frontend.core.designsystem.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.VerticalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import at.mocode.frontend.core.designsystem.theme.Dimens + +/** + * Ein standardisiertes Master-Detail Layout für die Stammdaten-Verwaltung. + * + * @param master Der Bereich auf der linken Seite (z.B. MsDataTable mit MsFilterBar). + * @param detail Der Bereich auf der rechten Seite (z.B. ein Editor-Formular). + * @param modifier Der Modifier für das gesamte Layout. + * @param masterWeight Der Anteil des Master-Bereichs (Standard: 0.4). + * @param detailHeader Optionaler Header für den Detail-Bereich (z.B. MsActionToolbar). + */ +@Composable +fun MsMasterDetailLayout( + master: @Composable BoxScope.() -> Unit, + detail: @Composable BoxScope.() -> Unit, + modifier: Modifier = Modifier, + masterWeight: Float = 0.4f, + detailHeader: @Composable (RowScope.() -> Unit)? = null +) { + Row(modifier = modifier.fillMaxSize()) { + // --- 1. Master-Bereich (z.B. Liste) --- + Box( + modifier = Modifier + .weight(masterWeight) + .fillMaxHeight() + .padding(Dimens.SpacingS) + ) { + master() + } + + // --- 2. Trennlinie (Vertikal) --- + VerticalDivider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant + ) + + // --- 3. Detail-Bereich (z.B. Editor) --- + Column( + modifier = Modifier + .weight(1f - masterWeight) + .fillMaxHeight() + ) { + // Optionaler Header für Aktionen + if (detailHeader != null) { + Surface( + color = MaterialTheme.colorScheme.surface, + tonalElevation = 1.dp + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Dimens.SpacingM, vertical = Dimens.SpacingS), + horizontalArrangement = Arrangement.End + ) { + detailHeader() + } + } + HorizontalDivider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant + ) + } + + Box( + modifier = Modifier + .fillMaxSize() + .padding(Dimens.SpacingM) + ) { + detail() + } + } + } +}