diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 239b407..0e5f0b4 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -42,7 +42,6 @@
Zamień składnik
- Zaplanuj
Porcje
Składniki
Kroki
@@ -86,6 +85,7 @@
Zaplanuj posiłek
+ Dodaj posiłek do planu
Wróć do szczegółów przepisu
Dodaj
Dodaj posiłek do planu
diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/components/button/CircleButton.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/components/button/CircleButton.kt
new file mode 100644
index 0000000..c0eabd2
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/components/button/CircleButton.kt
@@ -0,0 +1,75 @@
+package dev.ulfrx.recipe.ui.components.button
+
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.composeunstyled.UnstyledButton
+import com.composeunstyled.UnstyledIcon
+import dev.ulfrx.recipe.ui.theme.RecipeTheme
+
+@Composable
+fun CircleButton(
+ icon: ImageVector,
+ contentDescription: String,
+ modifier: Modifier = Modifier,
+ size: Dp = 48.dp,
+ tint: Color = RecipeTheme.colors.surface,
+ iconTint: Color = RecipeTheme.colors.content,
+ iconSize: Dp = 24.dp,
+ borderTint: Color = RecipeTheme.colors.borderCard,
+ borderWidth: Dp = 1.dp,
+ onClick: () -> Unit,
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isPressed by interactionSource.collectIsPressedAsState()
+
+ val scale by animateFloatAsState(
+ targetValue = if (isPressed) 1.15f else 1f,
+ animationSpec = tween(durationMillis = 120, easing = FastOutSlowInEasing),
+ label = "CircleGlassButton scale",
+ )
+
+ UnstyledButton(
+ onClick = onClick,
+ contentPadding = PaddingValues(0.dp),
+ interactionSource = interactionSource,
+ indication = null,
+ modifier = modifier
+ .scale(scale)
+ .size(size),
+ backgroundColor = tint,
+ borderColor = borderTint,
+ borderWidth = borderWidth,
+ shape = CircleShape,
+ ) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center,
+ ) {
+ UnstyledIcon(
+ imageVector = icon,
+ contentDescription = contentDescription,
+ tint = iconTint,
+ modifier = Modifier.size(iconSize),
+ )
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipedetail/RecipeDetailHero.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipedetail/RecipeDetailHero.kt
index a88bb26..ee44fe0 100644
--- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipedetail/RecipeDetailHero.kt
+++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipedetail/RecipeDetailHero.kt
@@ -5,7 +5,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
@@ -13,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
@@ -31,14 +29,14 @@ import androidx.compose.ui.unit.sp
import com.composables.icons.lucide.Calendar
import com.composables.icons.lucide.Clock
import com.composables.icons.lucide.Lucide
-import com.composeunstyled.UnstyledButton
import com.composeunstyled.UnstyledIcon
+import dev.ulfrx.recipe.ui.components.button.CircleButton
import dev.ulfrx.recipe.ui.theme.RecipeTheme
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import recipe.composeapp.generated.resources.Res
+import recipe.composeapp.generated.resources.meal_plan_editor_title_a11y
import recipe.composeapp.generated.resources.recipe_card_minutes_format
-import recipe.composeapp.generated.resources.recipe_detail_plan_button
import recipe.composeapp.generated.resources.sample_recipe
@Composable
@@ -83,38 +81,31 @@ internal fun RecipeDetailHero(
Spacer(Modifier.height(spacing.lg))
- BasicText(
- text = title,
- style =
- typography.display.copy(
- color = colors.content,
- fontSize = TITLE_FONT_SIZE,
- lineHeight = TITLE_LINE_HEIGHT,
- fontWeight = FontWeight.Bold,
- textAlign = TextAlign.Center,
- ),
- )
-
- Spacer(Modifier.height(spacing.lg))
-
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(spacing.sm),
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
+ Column(
+ modifier = Modifier.weight(1f),
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement =Arrangement.spacedBy(spacing.lg),
) {
- MetaChip(
- icon = Lucide.Clock,
- text = stringResource(Res.string.recipe_card_minutes_format, cookingMinutes),
+ BasicText(
+ text = title,
+ style =
+ typography.display.copy(
+ color = colors.content,
+ fontSize = TITLE_FONT_SIZE,
+ lineHeight = TITLE_LINE_HEIGHT,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Left,
+ ),
)
+ Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(spacing.sm)) {
+ MetaChip(
+ icon = Lucide.Clock,
+ text = stringResource(Res.string.recipe_card_minutes_format, cookingMinutes),
+ )
+ }
}
- PlanButton(
- text = stringResource(Res.string.recipe_detail_plan_button),
- onClick = onPlanClick,
- )
+ PlanButton(onClick = onPlanClick)
}
}
}
@@ -150,33 +141,19 @@ private fun MetaChip(
@Composable
private fun PlanButton(
- text: String,
onClick: () -> Unit,
) {
- val colors = RecipeTheme.colors
- UnstyledButton(
+ CircleButton(
onClick = onClick,
- shape = CHIP_SHAPE,
- backgroundColor = colors.accent,
- contentColor = colors.surface,
- contentPadding = PaddingValues(horizontal = PLAN_PADDING_H, vertical = PLAN_PADDING_V),
- ) {
- UnstyledIcon(
- imageVector = Lucide.Calendar,
- contentDescription = null,
- tint = colors.surface,
- modifier = Modifier.size(CHIP_ICON_SIZE),
- )
- Spacer(Modifier.width(CHIP_ICON_GAP))
- BasicText(
- text = text,
- style =
- RecipeTheme.typography.label.copy(
- color = colors.surface,
- fontWeight = FontWeight.Bold,
- ),
- )
- }
+ icon = Lucide.Calendar,
+ contentDescription = stringResource(Res.string.meal_plan_editor_title_a11y),
+ size = PLAN_BUTTON_SIZE,
+ iconSize = PLAN_BUTTON_ICON_SIZE,
+ tint = RecipeTheme.colors.surface,
+ iconTint = RecipeTheme.colors.accent,
+ borderTint = RecipeTheme.colors.borderCard,
+ borderWidth = 1.dp,
+ )
}
private const val BANNER_ASPECT_RATIO = 16f / 9f
@@ -187,15 +164,13 @@ private val BANNER_SHADOW_COLOR = Color.Black.copy(alpha = 0.45f)
// Leave room for the sheet handle (8dp top padding + 5dp handle) plus breathing room.
private val HERO_TOP_PADDING = 32.dp
-private val TITLE_FONT_SIZE = 24.sp
-private val TITLE_LINE_HEIGHT = 28.sp
+private val TITLE_FONT_SIZE = 19.sp
+private val TITLE_LINE_HEIGHT = 20.sp
private val CHIP_SHAPE = RoundedCornerShape(percent = 50)
private val CHIP_PADDING_H = 12.dp
private val CHIP_PADDING_V = 7.dp
private val CHIP_ICON_SIZE = 14.dp
private val CHIP_ICON_GAP = 5.dp
-
-// Plan button is slightly more padded than meta chips so it reads as a CTA, not just a coloured chip.
-private val PLAN_PADDING_H = 14.dp
-private val PLAN_PADDING_V = 9.dp
+private val PLAN_BUTTON_SIZE = 50.dp
+private val PLAN_BUTTON_ICON_SIZE = 25.dp