Fix meal planner editor

This commit is contained in:
2026-06-04 22:16:55 +02:00
parent d1916d3fe6
commit 4dd8ef5f8a
2 changed files with 129 additions and 100 deletions

View File

@@ -1,5 +1,6 @@
package dev.ulfrx.recipe.ui.screens.mealplaneditor package dev.ulfrx.recipe.ui.screens.mealplaneditor
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -41,6 +42,7 @@ import dev.ulfrx.recipe.ui.theme.RecipeTheme
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import recipe.composeapp.generated.resources.Res import recipe.composeapp.generated.resources.Res
import recipe.composeapp.generated.resources.meal_plan_editor_title
import recipe.composeapp.generated.resources.meal_plan_editor_section_ingredients import recipe.composeapp.generated.resources.meal_plan_editor_section_ingredients
import recipe.composeapp.generated.resources.meal_plan_editor_section_servings import recipe.composeapp.generated.resources.meal_plan_editor_section_servings
import recipe.composeapp.generated.resources.meal_plan_editor_section_slot import recipe.composeapp.generated.resources.meal_plan_editor_section_slot
@@ -94,94 +96,96 @@ internal fun MealPlanEditorContent(
editing.addedIngredients.mapTo(mutableSetOf()) { it.ingredientId } editing.addedIngredients.mapTo(mutableSetOf()) { it.ingredientId }
} }
Column( Box(modifier = Modifier.fillMaxSize().background(RecipeTheme.colors.background)) {
modifier = Column(
modifier
.fillMaxSize()
.verticalScroll(scrollState, enabled = !addPanelOpen),
) {
Spacer(Modifier.height(topChromeInset))
// Title sits in a row whose vertical bounds match the floating chrome
// (back/add buttons) so at scroll=0 it reads as the centre of the
// toolbar. Horizontal inset clears the chrome buttons — they are
// square pills of [topChromeHeight] anchored at spacing.lg.
Box(
modifier = modifier =
Modifier modifier
.fillMaxWidth() .fillMaxSize()
.height(topChromeHeight) .verticalScroll(scrollState, enabled = !addPanelOpen),
.padding(horizontal = spacing.lg + topChromeHeight + spacing.sm),
contentAlignment = Alignment.Center,
) { ) {
RecipeTitle(title = editing.recipe.title) Spacer(Modifier.height(topChromeInset))
} // Title sits in a row whose vertical bounds match the floating chrome
Spacer(Modifier.height(spacing.xl)) // (back/add buttons) so at scroll=0 it reads as the centre of the
RecipeCalendarPill( // toolbar. Horizontal inset clears the chrome buttons — they are
selectedDate = editing.selectedDate, // square pills of [topChromeHeight] anchored at spacing.lg.
expanded = editing.calendarExpanded, Box(
onExpandedChange = onSetCalendarExpanded, modifier =
onSelectDate = onSelectDate, Modifier
onSelectionShift = onSelectDate, .fillMaxWidth()
expandDirection = CalendarPillExpandDirection.Down, .height(topChromeHeight)
glass = false, .padding(horizontal = spacing.lg + topChromeHeight + spacing.sm),
tint = RecipeTheme.colors.surface, contentAlignment = Alignment.Center,
modifier = Modifier.padding(horizontal = spacing.lg), ) {
) RecipeTitle(recipeTitle = editing.recipe.title)
}
SectionContainer { Spacer(Modifier.height(spacing.xl))
SectionTitle(text = stringResource(Res.string.meal_plan_editor_section_slot)) RecipeCalendarPill(
Spacer(Modifier.height(spacing.sm)) selectedDate = editing.selectedDate,
MealSlotChipsRow( expanded = editing.calendarExpanded,
allSlots = MealSlot.entries, onExpandedChange = onSetCalendarExpanded,
allowedSlots = editing.recipe.allowedSlots, onSelectDate = onSelectDate,
selectedSlot = editing.selectedSlot, onSelectionShift = onSelectDate,
onSelectSlot = onSelectSlot, expandDirection = CalendarPillExpandDirection.Down,
glass = false,
tint = RecipeTheme.colors.surface,
modifier = Modifier.padding(horizontal = spacing.lg),
) )
}
SectionContainer { SectionContainer {
SectionTitle(text = stringResource(Res.string.nutrition_label)) SectionTitle(text = stringResource(Res.string.meal_plan_editor_section_slot))
Spacer(Modifier.height(spacing.sm)) Spacer(Modifier.height(spacing.sm))
NutritionSummary( MealSlotChipsRow(
nutrition = scaledNutrition, allSlots = MealSlot.entries,
modifier = Modifier.fillMaxWidth(), allowedSlots = editing.recipe.allowedSlots,
) selectedSlot = editing.selectedSlot,
} onSelectSlot = onSelectSlot,
)
}
SectionContainer { SectionContainer {
ServingsRow( SectionTitle(text = stringResource(Res.string.nutrition_label))
servings = editing.servings, Spacer(Modifier.height(spacing.sm))
onServingsChange = onSetServings, NutritionSummary(
) nutrition = scaledNutrition,
} modifier = Modifier.fillMaxWidth(),
)
}
SectionContainer { SectionContainer {
SectionTitle(text = stringResource(Res.string.meal_plan_editor_section_ingredients)) ServingsRow(
Spacer(Modifier.height(spacing.sm)) servings = editing.servings,
IngredientEditorList( onServingsChange = onSetServings,
recipeIngredients = editing.recipe.ingredients, )
addedIngredients = editing.addedIngredients, }
excludedIngredientIds = editing.excludedIngredients,
substitutions = editing.substitutions,
servings = editing.servings,
onSelectSubstitution = onSelectSubstitution,
onRemoveRecipeIngredient = onRemoveRecipeIngredient,
onRemoveAddedIngredient = onRemoveAddedIngredient,
onRestoreRemoved = onRestoreRemoved,
)
Spacer(Modifier.height(spacing.sm))
AddIngredientPanel(
catalog = catalog,
usedIngredientIds = usedIngredientIds,
onPick = onAddIngredient,
keyboardClearance = keyboardReserve + spacing.sm,
autoFocusEnabled = addPanelOpen,
keyboardAnimationDurationMillis = keyboardTransition.animationDurationMillis,
onOpenChange = { addPanelOpen = it },
)
}
Spacer(Modifier.height(bottomInset + spacing.xxl)) SectionContainer {
SectionTitle(text = stringResource(Res.string.meal_plan_editor_section_ingredients))
Spacer(Modifier.height(spacing.sm))
IngredientEditorList(
recipeIngredients = editing.recipe.ingredients,
addedIngredients = editing.addedIngredients,
excludedIngredientIds = editing.excludedIngredients,
substitutions = editing.substitutions,
servings = editing.servings,
onSelectSubstitution = onSelectSubstitution,
onRemoveRecipeIngredient = onRemoveRecipeIngredient,
onRemoveAddedIngredient = onRemoveAddedIngredient,
onRestoreRemoved = onRestoreRemoved,
)
Spacer(Modifier.height(spacing.sm))
AddIngredientPanel(
catalog = catalog,
usedIngredientIds = usedIngredientIds,
onPick = onAddIngredient,
keyboardClearance = keyboardReserve + spacing.sm,
autoFocusEnabled = addPanelOpen,
keyboardAnimationDurationMillis = keyboardTransition.animationDurationMillis,
onOpenChange = { addPanelOpen = it },
)
}
Spacer(Modifier.height(bottomInset + spacing.xxl))
}
} }
} }
@@ -224,22 +228,48 @@ private fun SectionContainer(content: @Composable () -> Unit) {
@Composable @Composable
private fun RecipeTitle( private fun RecipeTitle(
title: String, recipeTitle: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
BasicText( Column(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style =
RecipeTheme.typography.body.copy(
color = RecipeTheme.colors.content,
fontWeight = FontWeight.SemiBold,
fontSize = RecipeTitleSize,
textAlign = TextAlign.Center,
),
modifier = modifier, modifier = modifier,
) horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
BasicText(
text = stringResource(Res.string.meal_plan_editor_title),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style =
RecipeTheme.typography.body.copy(
color = RecipeTheme.colors.content,
fontWeight = FontWeight.Medium,
fontSize = RecipeTitleSize,
lineHeight = RecipeTitleLineHeight,
textAlign = TextAlign.Center,
),
modifier = Modifier.fillMaxWidth(),
)
Spacer(Modifier.height(RecipeTitleGap))
BasicText(
text = recipeTitle,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style =
RecipeTheme.typography.body.copy(
color = RecipeTheme.colors.contentMuted,
fontWeight = FontWeight.Normal,
fontSize = RecipeSubtitleSize,
lineHeight = RecipeSubtitleLineHeight,
textAlign = TextAlign.Center,
),
modifier = Modifier.fillMaxWidth(),
)
}
} }
private val RecipeTitleSize = 14.sp private val RecipeTitleSize = 16.sp
private val RecipeTitleLineHeight = 17.sp
private val RecipeTitleGap = 4.dp
private val RecipeSubtitleSize = 11.sp
private val RecipeSubtitleLineHeight = 14.sp

View File

@@ -35,14 +35,13 @@ fun recipeGlassFor(colors: RecipeColors): RecipeGlass =
frost = 0.dp, frost = 0.dp,
), ),
button = RecipeGlassStyle( button = RecipeGlassStyle(
refraction = 0.5f, refraction = 0.3f,
curve = 0.4f, curve = 0.2f,
edge = 0.03f, edge = 0.03f,
dispersion = 0.5f, dispersion = 0.5f,
saturation = 1f, saturation = 1f,
contrast = 1f, contrast = 0.85f,
frost = 15.dp, frost = 5.dp,
tint = colors.surfaceGlass,
), ),
panel = RecipeGlassStyle( panel = RecipeGlassStyle(
refraction = 0f, refraction = 0f,