@@ -28,9 +28,17 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
|
|
||||||
socketNotificationReceived(notification, payload) {
|
socketNotificationReceived(notification, payload) {
|
||||||
if (notification !== "STATE") return;
|
if (notification !== "STATE") return;
|
||||||
|
const previous = this.state;
|
||||||
this.state = payload;
|
this.state = payload;
|
||||||
this.setRecipeMode(Boolean(payload.active));
|
this.setRecipeMode(Boolean(payload.active));
|
||||||
this.updateDom(this.config.animationSpeed);
|
const requiresDomUpdate = previous.active !== payload.active
|
||||||
|
|| previous.recipeId !== payload.recipeId
|
||||||
|
|| previous.portions !== payload.portions
|
||||||
|
|| previous.theme !== payload.theme
|
||||||
|
|| previous.error !== payload.error
|
||||||
|
|| previous.recipe?.recipeYield !== payload.recipe?.recipeYield;
|
||||||
|
if (requiresDomUpdate) this.updateDom(this.config.animationSpeed);
|
||||||
|
else this.updateProgressDom();
|
||||||
},
|
},
|
||||||
|
|
||||||
notificationReceived(notification) {
|
notificationReceived(notification) {
|
||||||
@@ -86,6 +94,24 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateProgressDom() {
|
||||||
|
const root = document.getElementById(this.identifier)?.querySelector(".ncc-active");
|
||||||
|
if (!root) return;
|
||||||
|
root.querySelectorAll("[data-ncc-progress-kind]").forEach((item) => {
|
||||||
|
const kind = item.dataset.nccProgressKind;
|
||||||
|
const index = Number(item.dataset.nccProgressIndex);
|
||||||
|
const completed = kind === "ingredient"
|
||||||
|
? this.state.progress?.completedIngredients?.includes(index)
|
||||||
|
: this.state.progress?.completedSteps?.includes(index);
|
||||||
|
item.classList.toggle("ncc-completed", completed);
|
||||||
|
const checkbox = item.querySelector("input[type=checkbox]");
|
||||||
|
if (checkbox) {
|
||||||
|
checkbox.checked = completed;
|
||||||
|
checkbox.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getDom() {
|
getDom() {
|
||||||
const root = document.createElement("section");
|
const root = document.createElement("section");
|
||||||
root.className = `ncc-root ${this.state.active ? "ncc-active" : "ncc-idle"} ncc-theme-${this.state.theme || this.config.theme}`;
|
root.className = `ncc-root ${this.state.active ? "ncc-active" : "ncc-idle"} ncc-theme-${this.state.theme || this.config.theme}`;
|
||||||
@@ -143,9 +169,11 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
title.textContent = recipe.name || "Untitled recipe";
|
title.textContent = recipe.name || "Untitled recipe";
|
||||||
header.appendChild(title);
|
header.appendChild(title);
|
||||||
|
|
||||||
|
const yieldValue = recipe.originalRecipeYield && recipe.originalRecipeYield !== recipe.recipeYield
|
||||||
|
? `${recipe.recipeYield} (originally ${recipe.originalRecipeYield})`
|
||||||
|
: recipe.recipeYield;
|
||||||
const metadata = [
|
const metadata = [
|
||||||
["Yield", recipe.recipeYield],
|
["Yield", yieldValue],
|
||||||
["Original yield", recipe.originalRecipeYield !== recipe.recipeYield ? recipe.originalRecipeYield : null],
|
|
||||||
["Prep", NCCFormatDuration(recipe.prepTime)],
|
["Prep", NCCFormatDuration(recipe.prepTime)],
|
||||||
["Cook", NCCFormatDuration(recipe.cookTime)],
|
["Cook", NCCFormatDuration(recipe.cookTime)],
|
||||||
["Total", NCCFormatDuration(recipe.totalTime)]
|
["Total", NCCFormatDuration(recipe.totalTime)]
|
||||||
@@ -178,6 +206,8 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
const ingredientList = document.createElement("ul");
|
const ingredientList = document.createElement("ul");
|
||||||
(recipe.recipeIngredient || []).forEach((ingredient, index) => {
|
(recipe.recipeIngredient || []).forEach((ingredient, index) => {
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
|
item.dataset.nccProgressKind = "ingredient";
|
||||||
|
item.dataset.nccProgressIndex = index;
|
||||||
const status = recipe.recipeIngredientStatus?.[index];
|
const status = recipe.recipeIngredientStatus?.[index];
|
||||||
if (status === "unquantified") {
|
if (status === "unquantified") {
|
||||||
item.classList.add("ncc-quantity-warning");
|
item.classList.add("ncc-quantity-warning");
|
||||||
@@ -215,6 +245,8 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
const instructionList = document.createElement("ol");
|
const instructionList = document.createElement("ol");
|
||||||
this.flattenInstructions(recipe.recipeInstructions || []).forEach((step, index) => {
|
this.flattenInstructions(recipe.recipeInstructions || []).forEach((step, index) => {
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
|
item.dataset.nccProgressKind = "step";
|
||||||
|
item.dataset.nccProgressIndex = index;
|
||||||
const completed = progress.completedSteps?.includes(index);
|
const completed = progress.completedSteps?.includes(index);
|
||||||
if (completed) item.classList.add("ncc-completed");
|
if (completed) item.classList.add("ncc-completed");
|
||||||
const label = document.createElement("label");
|
const label = document.createElement("label");
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ link; it should look like `https://cloud.example.test/s/SHARE_TOKEN`.
|
|||||||
config: {
|
config: {
|
||||||
shareUrl: "https://cloud.example.test/s/SHARE_TOKEN",
|
shareUrl: "https://cloud.example.test/s/SHARE_TOKEN",
|
||||||
controlUrl: "http://mirror.example.test:8080/MMM-NextcloudCookbook/control",
|
controlUrl: "http://mirror.example.test:8080/MMM-NextcloudCookbook/control",
|
||||||
layout: "stacked"
|
layout: "stacked",
|
||||||
|
theme: "light"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -38,6 +39,9 @@ the controller and recipe overlay also offer a toggle, and the chosen theme is
|
|||||||
saved locally. After exiting recipe mode, use **Resume last recipe** in the
|
saved locally. After exiting recipe mode, use **Resume last recipe** in the
|
||||||
controller to restore its portions and completion marks.
|
controller to restore its portions and completion marks.
|
||||||
|
|
||||||
|
Ingredients and instructions can be marked complete from either the phone
|
||||||
|
controller or a touchscreen on the mirror; both views remain synchronized.
|
||||||
|
|
||||||
When portions change, the display shows both the selected yield and the
|
When portions change, the display shows both the selected yield and the
|
||||||
recipe's original yield. Ingredients without a leading quantity are marked in
|
recipe's original yield. Ingredients without a leading quantity are marked in
|
||||||
yellow because they cannot be scaled; unsupported or imprecise numeric scaling
|
yellow because they cannot be scaled; unsupported or imprecise numeric scaling
|
||||||
|
|||||||
+1
-2
@@ -106,7 +106,7 @@ function renderActiveRecipe() {
|
|||||||
activeRecipeName.textContent = recipe.name || "Untitled recipe";
|
activeRecipeName.textContent = recipe.name || "Untitled recipe";
|
||||||
const original = recipe.originalRecipeYield;
|
const original = recipe.originalRecipeYield;
|
||||||
activeRecipeYield.textContent = original && original !== recipe.recipeYield
|
activeRecipeYield.textContent = original && original !== recipe.recipeYield
|
||||||
? `Yield: ${recipe.recipeYield} (original: ${original})`
|
? `Yield: ${recipe.recipeYield} (originally ${original})`
|
||||||
: `Yield: ${recipe.recipeYield || "Not specified"}`;
|
: `Yield: ${recipe.recipeYield || "Not specified"}`;
|
||||||
renderChecklist(activeIngredients, recipe.recipeIngredient || [], progress.completedIngredients, "ingredient",
|
renderChecklist(activeIngredients, recipe.recipeIngredient || [], progress.completedIngredients, "ingredient",
|
||||||
recipe.recipeIngredientStatus || []);
|
recipe.recipeIngredientStatus || []);
|
||||||
@@ -162,7 +162,6 @@ async function updateProgress(kind, index, completed) {
|
|||||||
body: JSON.stringify({ kind, index, completed })
|
body: JSON.stringify({ kind, index, completed })
|
||||||
});
|
});
|
||||||
applyState(state);
|
applyState(state);
|
||||||
render();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
status.textContent = error.message;
|
status.textContent = error.message;
|
||||||
render();
|
render();
|
||||||
|
|||||||
+18
-2
@@ -122,8 +122,15 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checklist li { padding-left: .2rem; }
|
.checklist li { padding-left: .2rem; }
|
||||||
.checklist label { display: flex; gap: .6rem; align-items: flex-start; }
|
.checklist label { display: flex; gap: .6rem; align-items: flex-start; margin: 0; font-weight: normal; }
|
||||||
.checklist input { margin-top: .25rem; min-width: 1.1rem; min-height: 1.1rem; }
|
.checklist input {
|
||||||
|
width: 1.2rem;
|
||||||
|
min-width: 1.2rem;
|
||||||
|
height: 1.2rem;
|
||||||
|
min-height: 1.2rem;
|
||||||
|
margin: .18rem 0 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.checklist .completed { color: #777; text-decoration: line-through; }
|
.checklist .completed { color: #777; text-decoration: line-through; }
|
||||||
.checklist .quantity-warning { color: #876500; }
|
.checklist .quantity-warning { color: #876500; }
|
||||||
.checklist .quantity-error { color: #a2342b; }
|
.checklist .quantity-error { color: #a2342b; }
|
||||||
@@ -135,3 +142,12 @@ button {
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
main { padding: 18px 14px 44px; }
|
||||||
|
header { align-items: flex-start; flex-direction: column; }
|
||||||
|
.header-actions { width: 100%; justify-content: stretch; }
|
||||||
|
.header-actions button { flex: 1 1 auto; }
|
||||||
|
.active-recipe { margin: 1rem 0; padding: .85rem; }
|
||||||
|
.checklist { gap: .55rem; padding-left: 1.2rem; }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user