Add Nextcloud recipe display and controller
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
/* global Module, MM */
|
||||
|
||||
Module.register("MMM-NextcloudCookbook", {
|
||||
defaults: {
|
||||
controlPath: "/MMM-NextcloudCookbook/control",
|
||||
animationSpeed: 400
|
||||
},
|
||||
|
||||
start() {
|
||||
this.state = { active: false, recipe: null, error: null };
|
||||
this.hiddenModules = false;
|
||||
this.sendSocketNotification("CONFIG", this.config);
|
||||
},
|
||||
|
||||
getStyles() {
|
||||
return ["MMM-NextcloudCookbook.css"];
|
||||
},
|
||||
|
||||
socketNotificationReceived(notification, payload) {
|
||||
if (notification !== "STATE") return;
|
||||
this.state = payload;
|
||||
this.setRecipeMode(Boolean(payload.active));
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
|
||||
setRecipeMode(active) {
|
||||
if (active && !this.hiddenModules) {
|
||||
MM.getModules().exceptModule(this).enumerate((module) => {
|
||||
module.hide(this.config.animationSpeed, { lockString: this.name });
|
||||
});
|
||||
this.hiddenModules = true;
|
||||
} else if (!active && this.hiddenModules) {
|
||||
MM.getModules().exceptModule(this).enumerate((module) => {
|
||||
module.show(this.config.animationSpeed, { lockString: this.name });
|
||||
});
|
||||
this.hiddenModules = false;
|
||||
}
|
||||
},
|
||||
|
||||
getDom() {
|
||||
const root = document.createElement("section");
|
||||
root.className = this.state.active
|
||||
? "ncc-root ncc-active"
|
||||
: "ncc-root ncc-idle";
|
||||
|
||||
const qr = document.createElement("a");
|
||||
qr.className = "ncc-qr";
|
||||
qr.href = this.config.controlPath;
|
||||
qr.setAttribute("aria-label", "Open recipe controller");
|
||||
const qrImage = document.createElement("img");
|
||||
qrImage.src = "/MMM-NextcloudCookbook/qr.svg";
|
||||
qrImage.alt = "Recipe controller QR code";
|
||||
qr.appendChild(qrImage);
|
||||
root.appendChild(qr);
|
||||
|
||||
if (!this.state.active) return root;
|
||||
|
||||
if (this.state.error) {
|
||||
const error = document.createElement("p");
|
||||
error.className = "ncc-error";
|
||||
error.textContent = this.state.error;
|
||||
root.appendChild(error);
|
||||
return root;
|
||||
}
|
||||
|
||||
if (!this.state.recipe) {
|
||||
const loading = document.createElement("p");
|
||||
loading.className = "ncc-loading";
|
||||
loading.textContent = "Loading recipe…";
|
||||
root.appendChild(loading);
|
||||
return root;
|
||||
}
|
||||
|
||||
root.appendChild(this.renderRecipe(this.state.recipe));
|
||||
return root;
|
||||
},
|
||||
|
||||
renderRecipe(recipe) {
|
||||
const article = document.createElement("article");
|
||||
article.className = "ncc-recipe";
|
||||
|
||||
const header = document.createElement("header");
|
||||
const title = document.createElement("h1");
|
||||
title.textContent = recipe.name || "Untitled recipe";
|
||||
header.appendChild(title);
|
||||
|
||||
const metadata = [
|
||||
["Yield", recipe.recipeYield],
|
||||
["Prep", recipe.prepTime],
|
||||
["Cook", recipe.cookTime],
|
||||
["Total", recipe.totalTime]
|
||||
].filter(([, value]) => value);
|
||||
if (metadata.length) {
|
||||
const list = document.createElement("dl");
|
||||
list.className = "ncc-meta";
|
||||
metadata.forEach(([label, value]) => {
|
||||
const term = document.createElement("dt");
|
||||
term.textContent = label;
|
||||
const detail = document.createElement("dd");
|
||||
detail.textContent = value;
|
||||
list.append(term, detail);
|
||||
});
|
||||
header.appendChild(list);
|
||||
}
|
||||
article.appendChild(header);
|
||||
|
||||
const columns = document.createElement("div");
|
||||
columns.className = "ncc-columns";
|
||||
|
||||
const ingredients = document.createElement("section");
|
||||
const ingredientsTitle = document.createElement("h2");
|
||||
ingredientsTitle.textContent = "Ingredients";
|
||||
ingredients.appendChild(ingredientsTitle);
|
||||
const ingredientList = document.createElement("ul");
|
||||
(recipe.recipeIngredient || []).forEach((ingredient) => {
|
||||
const item = document.createElement("li");
|
||||
item.textContent = ingredient;
|
||||
ingredientList.appendChild(item);
|
||||
});
|
||||
ingredients.appendChild(ingredientList);
|
||||
|
||||
const instructions = document.createElement("section");
|
||||
const instructionsTitle = document.createElement("h2");
|
||||
instructionsTitle.textContent = "Instructions";
|
||||
instructions.appendChild(instructionsTitle);
|
||||
const instructionList = document.createElement("ol");
|
||||
this.flattenInstructions(recipe.recipeInstructions || []).forEach((step) => {
|
||||
const item = document.createElement("li");
|
||||
item.textContent = step;
|
||||
instructionList.appendChild(item);
|
||||
});
|
||||
instructions.appendChild(instructionList);
|
||||
|
||||
columns.append(ingredients, instructions);
|
||||
article.appendChild(columns);
|
||||
return article;
|
||||
},
|
||||
|
||||
flattenInstructions(instructions) {
|
||||
return instructions.flatMap((instruction) => {
|
||||
if (typeof instruction === "string") return [instruction];
|
||||
if (Array.isArray(instruction.itemListElement)) {
|
||||
return this.flattenInstructions(instruction.itemListElement);
|
||||
}
|
||||
return instruction.text ? [instruction.text] : [];
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user