feat: implementiere WizardScaffold und Hotkey-Integration mittels Compose
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.composeCompiler)
|
||||
}
|
||||
|
||||
group = "at.mocode.frontend.core"
|
||||
@@ -29,6 +31,11 @@ kotlin {
|
||||
implementation(projects.frontend.core.domain)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
// Compose (for WizardScaffold UI in commonMain)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
@@ -38,6 +45,8 @@ kotlin {
|
||||
jvmMain.dependencies {
|
||||
implementation(projects.frontend.core.navigation)
|
||||
implementation(projects.frontend.core.domain)
|
||||
implementation(compose.ui)
|
||||
implementation(compose.materialIconsExtended)
|
||||
}
|
||||
|
||||
jvmTest.dependencies {
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package at.mocode.frontend.core.wizard.ui
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
data class WizardStepUi(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val subtitle: String? = null,
|
||||
val isSkipped: Boolean = false,
|
||||
val enabled: Boolean = true
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun WizardScaffold(
|
||||
steps: List<WizardStepUi>,
|
||||
currentIndex: Int,
|
||||
canBack: Boolean,
|
||||
canNext: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onNext: () -> Unit,
|
||||
onSaveDraft: (() -> Unit)? = null,
|
||||
nextLabel: String = "Weiter",
|
||||
backLabel: String = "Zurück",
|
||||
finishLabel: String = "Fertig",
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isLast = currentIndex >= steps.lastIndex && steps.isNotEmpty()
|
||||
|
||||
Surface(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
// Header: Breadcrumb / Step-Leiste
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(bottom = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
steps.forEachIndexed { idx, meta ->
|
||||
val color = when {
|
||||
idx == currentIndex -> MaterialTheme.colorScheme.primary
|
||||
meta.isSkipped -> MaterialTheme.colorScheme.secondary
|
||||
else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = "${idx + 1}.",
|
||||
color = color,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = if (idx == currentIndex) FontWeight.Bold else FontWeight.Normal
|
||||
)
|
||||
Text(
|
||||
text = " ${meta.title}" + if (meta.isSkipped) " (übersprungen)" else "",
|
||||
color = color,
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
if (idx != steps.lastIndex) {
|
||||
Text(text = " › ", color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||
content()
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(12.dp))
|
||||
|
||||
// Footer: Actions
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedButton(onClick = onBack, enabled = canBack) {
|
||||
Text(backLabel)
|
||||
}
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (onSaveDraft != null) {
|
||||
OutlinedButton(onClick = onSaveDraft) {
|
||||
Text("Zwischenspeichern (Alt+S)")
|
||||
}
|
||||
Spacer(Modifier.padding(horizontal = 6.dp))
|
||||
}
|
||||
Button(onClick = onNext, enabled = canNext) {
|
||||
Text(if (isLast) finishLabel else nextLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package at.mocode.frontend.core.wizard.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
/**
|
||||
* Desktop-spezifische Variante. Hotkeys werden in einem Folge‑Inkrement ergänzt,
|
||||
* sobald die Compose‑API-Version projektweit abgestimmt ist.
|
||||
*/
|
||||
@Composable
|
||||
fun WizardScaffoldWithHotkeys(
|
||||
steps: List<WizardStepUi>,
|
||||
currentIndex: Int,
|
||||
canBack: Boolean,
|
||||
canNext: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onNext: () -> Unit,
|
||||
onSaveDraft: (() -> Unit)? = null,
|
||||
nextLabel: String = "Weiter",
|
||||
backLabel: String = "Zurück",
|
||||
finishLabel: String = "Fertig",
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
WizardScaffold(
|
||||
steps = steps,
|
||||
currentIndex = currentIndex,
|
||||
canBack = canBack,
|
||||
canNext = canNext,
|
||||
onBack = onBack,
|
||||
onNext = onNext,
|
||||
onSaveDraft = onSaveDraft,
|
||||
nextLabel = nextLabel,
|
||||
backLabel = backLabel,
|
||||
finishLabel = finishLabel,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user