Optimize servings control

This commit is contained in:
araemer 2025-10-21 07:36:49 +02:00
parent e6ea18bef8
commit f980d4d86d
4 changed files with 220 additions and 166 deletions

View file

@ -1,5 +1,6 @@
/* Import Tailwind layers */ /* Import Tailwind layers */
@import "tailwindcss"; @import "tailwindcss";
@tailwind utilities; @tailwind utilities;
/* Custom recipe app styles */ /* Custom recipe app styles */
@ -11,10 +12,14 @@
} }
.content-container { .content-container {
@apply bg-gray-100 w-full min-h-screen max-w-6xl shadow-xl p-8 @apply bg-gray-100 w-full min-h-screen max-w-6xl shadow-xl p-8;
} }
/* headings */ /* headings */
.sticky-header {
@apply sticky bg-gray-100 top-0 left-0 right-0 pb-6 border-b-2 border-gray-300;
}
.content-title { .content-title {
@apply text-3xl font-black mb-8 text-blue-900; @apply text-3xl font-black mb-8 text-blue-900;
} }
@ -47,27 +52,35 @@
.basic-button { .basic-button {
@apply px-4 py-2 shadow-md rounded-lg whitespace-nowrap; @apply px-4 py-2 shadow-md rounded-lg whitespace-nowrap;
} }
.default-button-bg { .default-button-bg {
@apply bg-gray-300 hover:bg-gray-400; @apply bg-gray-300 hover:bg-gray-400;
} }
.default-button-text { .default-button-text {
@apply text-gray-600; @apply text-gray-600;
} }
.primary-button-bg { .primary-button-bg {
@apply bg-blue-300 hover:bg-blue-400; @apply bg-blue-300 hover:bg-blue-400;
} }
.primary-button-text { .primary-button-text {
@apply text-gray-600; @apply text-gray-600;
} }
.dark-button-bg { .dark-button-bg {
@apply bg-gray-600 hover:bg-gray-800; @apply bg-gray-600 hover:bg-gray-800;
} }
.dark-button-text { .dark-button-text {
@apply text-white; @apply text-white;
} }
.transparent-button-bg { .transparent-button-bg {
@apply bg-transparent hover:bg-transparent; @apply bg-transparent hover:bg-transparent;
} }
.transparent-button-text { .transparent-button-text {
@apply text-gray-600; @apply text-gray-600;
} }

View file

@ -77,7 +77,7 @@ export default function RecipeDetailPage() {
{/* Container defining the maximum width of the content */} {/* Container defining the maximum width of the content */}
<div className="content-container"> <div className="content-container">
{/* Header - remains in position when scrolling */} {/* Header - remains in position when scrolling */}
<div className="sticky bg-gray-100 top-0 left-0 right-0 pb-6 border-b-2 border-gray-300"> <div className="sticky-header">
<h1 className="content-title mb-0">{recipeWorkingCopy.title}</h1> <h1 className="content-title mb-0">{recipeWorkingCopy.title}</h1>
</div> </div>
@ -93,19 +93,44 @@ export default function RecipeDetailPage() {
)} )}
{/* Servings */} {/* Servings */}
<div className="flex flex-row items-center gap-2 bg-blue-100 columns-2 rounded p-2 mb-4"> <div
<p className="mb-2">For {recipeWorkingCopy.servings.amount} {recipeWorkingCopy.servings.unit}</p> className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 bg-gray-200 rounded p-3 mb-4">
<p>
Für {recipeWorkingCopy.servings.amount} {recipeWorkingCopy.servings.unit}
</p>
<div className="flex items-center justify-end sm:justify-center gap-2">
{/* Minus button */}
<button
type="button"
onClick={() => recalculateIngredients(Math.max(1, recipeWorkingCopy.servings.amount - 1))}
className="enumeration-indicator primary-button-bg"
>
</button>
{/* Number input (no spin buttons) */}
<input <input
type="number" type="number"
className="input-field w-20 ml-auto" inputMode="numeric"
pattern="[0-9]*"
className="w-16 text-center input-field"
value={recipeWorkingCopy.servings.amount} value={recipeWorkingCopy.servings.amount}
onChange={ onChange={e => recalculateIngredients(Number(e.target.value))}
e => { min={1}
recalculateIngredients(Number(e.target.value))
}
}
/> />
{/* Plus button */}
<button
type="button"
onClick={() => recalculateIngredients(recipeWorkingCopy.servings.amount + 1)}
className="enumeration-indicator primary-button-bg"
>
+
</button>
</div> </div>
</div>
{/* Ingredients */} {/* Ingredients */}
<h2 className="section-heading">Zutaten</h2> <h2 className="section-heading">Zutaten</h2>
<ul> <ul>

View file

@ -36,14 +36,16 @@ export default function RecipeListPage() {
navigate(getRecipeAddUrl()) navigate(getRecipeAddUrl())
} }
if(!recipeList) { return <div>Loading!</div>} if (!recipeList) {
return <div>Loading!</div>
}
return ( return (
/*Container spanning entire screen used to center content horizontally */ /*Container spanning entire screen used to center content horizontally */
<div className="app-bg"> <div className="app-bg">
{/* Container defining the maximum width of the content */} {/* Container defining the maximum width of the content */}
<div className="content-container"> <div className="content-container">
{/* Header - remains in position when scrolling */} {/* Header - remains in position when scrolling */}
<div className="sticky bg-gray-100 top-0 left-0 right-0 pb-4 border-b-2 border-gray-300"> <div className="sticky-header">
<h1 className="content-title">Recipes</h1> <h1 className="content-title">Recipes</h1>
<RecipeListToolbar <RecipeListToolbar
onAddClicked={handleAdd} onAddClicked={handleAdd}
@ -53,7 +55,8 @@ export default function RecipeListPage() {
</div> </div>
{/*Content - List of recipe cards */} {/*Content - List of recipe cards */}
<div className="w-full pt-4"> <div className="w-full pt-4">
<div className="grid gap-6 grid-cols-[repeat(auto-fit,minmax(220px,auto))] max-w-6xl mx-auto justify-center"> <div
className="grid gap-6 grid-cols-[repeat(auto-fit,minmax(220px,auto))] max-w-6xl mx-auto justify-center">
{recipeList.map((recipe) => ( {recipeList.map((recipe) => (
<RecipeListItem <RecipeListItem
key={recipe.id} key={recipe.id}

View file

@ -24,6 +24,19 @@ body {
min-height: 100vh; min-height: 100vh;
} }
/* Hide number input spinners (Chrome, Safari, Edge, Opera) */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Hide number input spinners in Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
/*a { /*a {
font-weight: 500; font-weight: 500;
color: #646cff; color: #646cff;