Add portion controls and improve recipe display
This commit is contained in:
@@ -3,8 +3,14 @@ const status = document.querySelector("#status");
|
||||
const list = document.querySelector("#recipes");
|
||||
const search = document.querySelector("#search");
|
||||
const exit = document.querySelector("#exit");
|
||||
const portionControls = document.querySelector("#portions");
|
||||
const portionCount = document.querySelector("#portion-count");
|
||||
const fewerPortions = document.querySelector("#fewer-portions");
|
||||
const morePortions = document.querySelector("#more-portions");
|
||||
let recipes = [];
|
||||
let activeRecipeId = null;
|
||||
let activeRecipeName = null;
|
||||
let portions = null;
|
||||
|
||||
async function request(path, options) {
|
||||
const response = await fetch(`${apiBase}${path}`, {
|
||||
@@ -39,6 +45,9 @@ function render() {
|
||||
return item;
|
||||
}));
|
||||
if (!matches.length) status.textContent = "No matching recipes.";
|
||||
portionControls.hidden = !activeRecipeId;
|
||||
portionCount.textContent = portions ?? "";
|
||||
fewerPortions.disabled = portions <= 1;
|
||||
}
|
||||
|
||||
async function activate(recipe) {
|
||||
@@ -49,6 +58,8 @@ async function activate(recipe) {
|
||||
body: JSON.stringify({ recipeId: recipe.id })
|
||||
});
|
||||
activeRecipeId = state.recipeId;
|
||||
activeRecipeName = recipe.name;
|
||||
portions = state.portions;
|
||||
status.textContent = `${recipe.name} is now on the mirror.`;
|
||||
render();
|
||||
} catch (error) {
|
||||
@@ -56,11 +67,30 @@ async function activate(recipe) {
|
||||
}
|
||||
}
|
||||
|
||||
async function adjustPortions(delta) {
|
||||
try {
|
||||
const state = await request("/portions", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ delta })
|
||||
});
|
||||
portions = state.portions;
|
||||
status.textContent = `${activeRecipeName} is now on the mirror.`;
|
||||
render();
|
||||
} catch (error) {
|
||||
status.textContent = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
fewerPortions.addEventListener("click", () => adjustPortions(-1));
|
||||
morePortions.addEventListener("click", () => adjustPortions(1));
|
||||
|
||||
exit.addEventListener("click", async () => {
|
||||
status.textContent = "Leaving recipe mode…";
|
||||
try {
|
||||
await request("/exit", { method: "POST", body: "{}" });
|
||||
activeRecipeId = null;
|
||||
activeRecipeName = null;
|
||||
portions = null;
|
||||
status.textContent = "The mirror is back to its normal display.";
|
||||
render();
|
||||
} catch (error) {
|
||||
@@ -73,6 +103,8 @@ search.addEventListener("input", render);
|
||||
Promise.all([request("/state"), request("/recipes")])
|
||||
.then(([state, loadedRecipes]) => {
|
||||
activeRecipeId = state.recipeId;
|
||||
activeRecipeName = state.recipe?.name || null;
|
||||
portions = state.portions;
|
||||
recipes = loadedRecipes;
|
||||
status.textContent = `${recipes.length} recipes available.`;
|
||||
render();
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<button id="exit" class="secondary" type="button">Exit recipe mode</button>
|
||||
</header>
|
||||
<p id="status" role="status">Loading recipes…</p>
|
||||
<div id="portions" class="portions" hidden>
|
||||
<button id="fewer-portions" type="button" aria-label="One fewer portion">−</button>
|
||||
<span><strong id="portion-count"></strong> portions</span>
|
||||
<button id="more-portions" type="button" aria-label="One more portion">+</button>
|
||||
</div>
|
||||
<label for="search">Find a recipe</label>
|
||||
<input id="search" type="search" autocomplete="off" placeholder="Search recipes">
|
||||
<ul id="recipes" aria-live="polite"></ul>
|
||||
|
||||
@@ -61,6 +61,28 @@ button {
|
||||
|
||||
#status { min-height: 1.5em; color: #665a45; }
|
||||
|
||||
.portions {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
margin: 4px 0 20px;
|
||||
}
|
||||
|
||||
.portions[hidden] { display: none; }
|
||||
|
||||
.portions button {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
color: #fff;
|
||||
background: #5a4b30;
|
||||
font-size: 1.6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.portions button:disabled { cursor: default; opacity: .4; }
|
||||
|
||||
.portions span { min-width: 92px; text-align: center; }
|
||||
|
||||
#recipes { display: grid; gap: 10px; padding: 0; list-style: none; }
|
||||
|
||||
.recipe {
|
||||
|
||||
Reference in New Issue
Block a user