app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 643337eb6af242892777fb9f4b00da642f811b18
parent c7af77f8e33e47e8c8ebc26c752ba515ba8e6b75
Author: triesap <tyson@radroots.org>
Date:   Tue, 11 Aug 2026 16:23:04 +0000

ui: implement the dashboard scaffold

- keep the top bar sidebar and main header fixed
- bound the main body without page-level scrolling
- place a full inspector beside the useful center width
- overlay the minimum inspector at compact desktop width

Diffstat:
Aapp/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/DashboardScaffold.kt | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/DashboardScaffoldTest.kt | 48++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+), 0 deletions(-)

diff --git a/app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/DashboardScaffold.kt b/app/shared/src/commonMain/kotlin/org/harvestcircle/ui/shell/DashboardScaffold.kt @@ -0,0 +1,64 @@ +package org.harvestcircle.ui.shell + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp + +@Composable +fun DashboardScaffold( + windowWidthDp: Int, + inspectorVisible: Boolean, + topBar: @Composable () -> Unit, + sidebar: @Composable () -> Unit, + mainHeader: @Composable () -> Unit, + mainBody: @Composable () -> Unit, + inspector: @Composable () -> Unit = {}, +) { + val placement = inspectorPlacement(windowWidthDp, inspectorVisible) + Column(Modifier.fillMaxSize().testTag("dashboard-scaffold")) { + Region("dashboard-top-bar", "Global top bar", Modifier.fillMaxWidth().height(56.dp), topBar) + Row(Modifier.fillMaxSize()) { + Region("dashboard-sidebar", "Workspace sidebar", Modifier.width(232.dp).fillMaxHeight(), sidebar) + Column(Modifier.weight(1f).fillMaxHeight()) { + Region("dashboard-main-header", "Main panel header", Modifier.fillMaxWidth().height(56.dp), mainHeader) + Region("dashboard-main-body", "Main panel body", Modifier.fillMaxSize(), mainBody) + } + if (placement == InspectorPlacement.Beside) { + Region("dashboard-inspector-beside", "Inspector", Modifier.width(400.dp).fillMaxHeight(), inspector) + } + } + } + if (placement == InspectorPlacement.Overlay) { + Box(Modifier.fillMaxSize(), contentAlignment = androidx.compose.ui.Alignment.CenterEnd) { + Region("dashboard-inspector-overlay", "Inspector", Modifier.width(360.dp).fillMaxHeight(), inspector) + } + } +} + +@Composable +private fun Region( + tag: String, + label: String, + modifier: Modifier, + content: @Composable () -> Unit, +) { + Box( + modifier = + modifier + .semantics { contentDescription = label } + .testTag(tag), + ) { + content() + } +} diff --git a/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/DashboardScaffoldTest.kt b/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/DashboardScaffoldTest.kt @@ -0,0 +1,48 @@ +package org.harvestcircle.ui.shell + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.v2.runComposeUiTest +import androidx.compose.ui.unit.dp +import kotlin.test.Test + +@OptIn(ExperimentalTestApi::class) +class DashboardScaffoldTest { + @Test + fun preferredWindowKeepsFixedRegionsAndInspectorBeside() = + runComposeUiTest { + setContent { dashboard(width = 1280) } + onNodeWithTag("dashboard-top-bar").assertIsDisplayed() + onNodeWithTag("dashboard-sidebar").assertIsDisplayed() + onNodeWithTag("dashboard-main-header").assertIsDisplayed() + onNodeWithTag("dashboard-main-body").assertIsDisplayed() + onNodeWithTag("dashboard-inspector-beside").assertIsDisplayed() + } + + @Test + fun minimumWindowMovesInspectorToOverlay() = + runComposeUiTest { + setContent { dashboard(width = 1100) } + onNodeWithTag("dashboard-inspector-overlay").assertIsDisplayed() + onNodeWithTag("dashboard-main-body").assertIsDisplayed() + } +} + +@androidx.compose.runtime.Composable +private fun dashboard(width: Int) { + Box(Modifier.requiredSize(width.dp, 720.dp)) { + DashboardScaffold( + windowWidthDp = width, + inspectorVisible = true, + topBar = {}, + sidebar = {}, + mainHeader = {}, + mainBody = {}, + inspector = {}, + ) + } +}