Add dynamic sizing of recipe
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-active {
|
.MMM-NextcloudCookbook .ncc-active {
|
||||||
|
--ncc-type-scale: 1;
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
@@ -49,9 +51,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-recipe h1 {
|
.MMM-NextcloudCookbook .ncc-recipe h1 {
|
||||||
margin: 0 220px 26px 0;
|
margin: 0 220px calc(26px * var(--ncc-type-scale)) 0;
|
||||||
color: #fff6d8;
|
color: #fff6d8;
|
||||||
font-size: 82px;
|
font-size: calc(82px * var(--ncc-type-scale));
|
||||||
line-height: 1.08;
|
line-height: 1.08;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +61,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px 22px;
|
gap: 10px 22px;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
margin: 0 0 36px;
|
margin: 0 0 calc(36px * var(--ncc-type-scale));
|
||||||
font-size: 36px;
|
font-size: calc(36px * var(--ncc-type-scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-meta dt {
|
.MMM-NextcloudCookbook .ncc-meta dt {
|
||||||
@@ -79,21 +81,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-columns h2 {
|
.MMM-NextcloudCookbook .ncc-columns h2 {
|
||||||
margin: 0 0 18px;
|
margin: 0 0 calc(18px * var(--ncc-type-scale));
|
||||||
color: #d6b96b;
|
color: #d6b96b;
|
||||||
font-size: 56px;
|
font-size: calc(56px * var(--ncc-type-scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-columns ul,
|
.MMM-NextcloudCookbook .ncc-columns ul,
|
||||||
.MMM-NextcloudCookbook .ncc-columns ol {
|
.MMM-NextcloudCookbook .ncc-columns ol {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 1.25em;
|
padding-left: 1.25em;
|
||||||
font-size: 50px;
|
font-size: calc(50px * var(--ncc-type-scale));
|
||||||
line-height: 1.36;
|
line-height: 1.36;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-columns li {
|
.MMM-NextcloudCookbook .ncc-columns li {
|
||||||
margin-bottom: 18px;
|
margin-bottom: calc(18px * var(--ncc-type-scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
.MMM-NextcloudCookbook .ncc-error {
|
.MMM-NextcloudCookbook .ncc-error {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
start() {
|
start() {
|
||||||
this.state = { active: false, recipe: null, error: null };
|
this.state = { active: false, recipe: null, error: null };
|
||||||
this.hiddenModules = false;
|
this.hiddenModules = false;
|
||||||
|
window.addEventListener("resize", () => this.scheduleRecipeFit());
|
||||||
this.sendSocketNotification("CONFIG", this.config);
|
this.sendSocketNotification("CONFIG", this.config);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -30,6 +31,45 @@ Module.register("MMM-NextcloudCookbook", {
|
|||||||
this.updateDom(this.config.animationSpeed);
|
this.updateDom(this.config.animationSpeed);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
notificationReceived(notification) {
|
||||||
|
if (notification === "MODULE_DOM_UPDATED") this.scheduleRecipeFit();
|
||||||
|
},
|
||||||
|
|
||||||
|
scheduleRecipeFit() {
|
||||||
|
window.requestAnimationFrame(() => this.fitRecipe());
|
||||||
|
},
|
||||||
|
|
||||||
|
fitRecipe() {
|
||||||
|
const root = document.getElementById(this.identifier)?.querySelector(".ncc-active");
|
||||||
|
const recipe = root?.querySelector(".ncc-recipe");
|
||||||
|
if (!root || !recipe) return;
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(root);
|
||||||
|
const availableHeight = root.clientHeight
|
||||||
|
- Number.parseFloat(style.paddingTop)
|
||||||
|
- Number.parseFloat(style.paddingBottom);
|
||||||
|
const availableWidth = root.clientWidth
|
||||||
|
- Number.parseFloat(style.paddingLeft)
|
||||||
|
- Number.parseFloat(style.paddingRight);
|
||||||
|
let minimum = 0.45;
|
||||||
|
let maximum = 2;
|
||||||
|
let best = minimum;
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt < 12; attempt++) {
|
||||||
|
const scale = (minimum + maximum) / 2;
|
||||||
|
root.style.setProperty("--ncc-type-scale", scale);
|
||||||
|
const fits = recipe.scrollHeight <= availableHeight
|
||||||
|
&& recipe.scrollWidth <= availableWidth;
|
||||||
|
if (fits) {
|
||||||
|
best = scale;
|
||||||
|
minimum = scale;
|
||||||
|
} else {
|
||||||
|
maximum = scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.style.setProperty("--ncc-type-scale", best.toFixed(3));
|
||||||
|
},
|
||||||
|
|
||||||
setRecipeMode(active) {
|
setRecipeMode(active) {
|
||||||
if (active && !this.hiddenModules) {
|
if (active && !this.hiddenModules) {
|
||||||
MM.getModules().exceptModule(this).enumerate((module) => {
|
MM.getModules().exceptModule(this).enumerate((module) => {
|
||||||
|
|||||||
@@ -98,6 +98,50 @@ References:
|
|||||||
- [Cookbook API documentation](https://nextcloud.github.io/cookbook/dev/api/0.1.0/index.html)
|
- [Cookbook API documentation](https://nextcloud.github.io/cookbook/dev/api/0.1.0/index.html)
|
||||||
- [Cookbook user documentation](https://nextcloud.github.io/cookbook/user/)
|
- [Cookbook user documentation](https://nextcloud.github.io/cookbook/user/)
|
||||||
|
|
||||||
|
## Future architecture: recipe sources and consumers
|
||||||
|
|
||||||
|
The current module both retrieves recipes and presents the full-screen recipe
|
||||||
|
mode. A future version should separate those responsibilities using
|
||||||
|
MagicMirror's notification system as a shared recipe data stream:
|
||||||
|
|
||||||
|
```text
|
||||||
|
MMM-NextcloudCookbook ─┐
|
||||||
|
MMM-Paprika ───────────┼── recipe data notifications ──> MMM-RecipeMode
|
||||||
|
MMM-Mealime ───────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
In that model:
|
||||||
|
|
||||||
|
- `MMM-NextcloudCookbook` is a source adapter. It reads recipes from Nextcloud
|
||||||
|
Cookbook and publishes normalized recipe records.
|
||||||
|
- `MMM-RecipeMode` is a consumer. It provides recipe selection, the phone
|
||||||
|
control page, portion scaling, QR code, and full-screen cooking display.
|
||||||
|
- Other source modules can publish recipes from Paprika, Mealime, local files,
|
||||||
|
or other services without duplicating the presentation code.
|
||||||
|
- Additional consumers can use the same recipe data for meal planning, search,
|
||||||
|
shopping lists, dashboards, or other displays.
|
||||||
|
|
||||||
|
The shared notification contract should eventually define source-qualified
|
||||||
|
stable recipe IDs, basic list metadata, complete recipe records, refresh and
|
||||||
|
error states, and optional source capabilities. The normalized record should
|
||||||
|
retain schema.org Recipe fields where practical. Exact notification names and
|
||||||
|
payload schemas should be designed when a second source or consumer is
|
||||||
|
implemented, rather than treating the current module's private HTTP API as the
|
||||||
|
cross-module contract.
|
||||||
|
|
||||||
|
This separation is a future feature. The current combined behavior should
|
||||||
|
remain functional until `MMM-RecipeMode` can replace its presentation and
|
||||||
|
control responsibilities.
|
||||||
|
|
||||||
|
### Future presentation option: preserve selected modules
|
||||||
|
|
||||||
|
Recipe mode currently hides every other MagicMirror module. A future
|
||||||
|
`MMM-RecipeMode` configuration should accept an allowlist of modules to leave
|
||||||
|
visible while cooking. For example, a user could preserve a calendar at the top
|
||||||
|
of the display while the recipe occupies the remaining area. The option should
|
||||||
|
support module names and, where multiple instances exist, specific instance
|
||||||
|
identifiers. Hiding all other modules should remain the default.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user