update zutatenliste
This commit is contained in:
68
index.html
Normal file
68
index.html
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Kartoffelsalat-Rechner</title>
|
||||||
|
<script defer src="js/main.js" type="module"></script>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="window" x-data="daten">
|
||||||
|
<div class="actionbar">
|
||||||
|
<button type="button" @click="reset()">Alles zurücksetzen</button>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="search"
|
||||||
|
placeholder="Suche..."
|
||||||
|
x-model="filter"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid">
|
||||||
|
<template x-for="(z) in zutaten_filter">
|
||||||
|
<div class="grid-element">
|
||||||
|
<template x-if="!z.gruppe">
|
||||||
|
<div class="zutat">
|
||||||
|
<span class="title" x-text="z.name"></span>
|
||||||
|
<div class="flexbox">
|
||||||
|
<button type="button" class="minus" @click="menge(-1,z.id)">
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
x-text="z.menge"
|
||||||
|
:class="z.menge > 0 ? 'bold' : ''"
|
||||||
|
></span>
|
||||||
|
<button type="button" class="plus" @click="menge(+1,z.id)">
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template x-if="z.gruppe">
|
||||||
|
<div class="heading">
|
||||||
|
<h2 x-text="z.gruppe" class="title"></h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="recipe">
|
||||||
|
<div class="heading">
|
||||||
|
<h2 class="title">Rezept für "<span x-text="rezeptname"></span>":</h2>
|
||||||
|
<ul>
|
||||||
|
<template x-for="z in rezept">
|
||||||
|
<li x-text="z"></li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
<template x-if="rezept.length == 0">
|
||||||
|
<i
|
||||||
|
>Wähle ein paar Zutaten aus der Liste aus. Dein personalisiertes
|
||||||
|
Rezept erscheint dann hier...</i
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<!-- <pre x-text="JSON.stringify(zutaten, null, 2)" x-if="false"></pre> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
125
js/main.js
Normal file
125
js/main.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import Alpine from "https://unpkg.com/alpinejs@3.15.4/dist/module.esm.js";
|
||||||
|
window.Alpine = Alpine;
|
||||||
|
Alpine.data("daten", () => ({
|
||||||
|
zutaten: [
|
||||||
|
{ gruppe: "Grundzutaten" },
|
||||||
|
{ name: "Kartoffeln", einheit: "g", faktor: 200 },
|
||||||
|
{ name: "Nudeln", einheit: "g", faktor: 50 },
|
||||||
|
{ name: "Salatgurke", einheit: "x", faktor: 0.5, runde: 10 },
|
||||||
|
{ name: "Saure Gurke", einheit: "x", faktor: 2 },
|
||||||
|
{ name: "Mayonnaise", einheit: "g", faktor: 20 },
|
||||||
|
{ name: "Senf", einheit: " EL", faktor: 1.4 },
|
||||||
|
{ name: "Ketchup", einheit: " EL", faktor: 1.4 },
|
||||||
|
{ name: "Saure Sahne", einheit: "ml", faktor: 100 },
|
||||||
|
{ gruppe: "Früchte" },
|
||||||
|
{ name: "Birne", einheit: "x", faktor: 1 },
|
||||||
|
{ name: "Apfel", einheit: "x", faktor: 1 },
|
||||||
|
{ name: "Rote Beete", einheit: " Knolle/n", faktor: 1 },
|
||||||
|
{ name: "Mandarine", einheit: "x", faktor: 1 },
|
||||||
|
{ name: "Ananas", einheit: "g", faktor: 50 },
|
||||||
|
{ name: "Mango", einheit: "g", faktor: 50 },
|
||||||
|
{ gruppe: "Fleischanteil" },
|
||||||
|
{ name: "Fleischwurst", einheit: "g", faktor: 200 },
|
||||||
|
{ name: "Hackfleisch", einheit: "g", faktor: 250 },
|
||||||
|
{ name: "Bratwurst", einheit: "x", faktor: 1 },
|
||||||
|
{ name: "Hering", einheit: "g", faktor: 150 },
|
||||||
|
{ name: "Thunfisch", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Lachs", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Mehlwürmer" },
|
||||||
|
{ gruppe: "Milchprodukte" },
|
||||||
|
{ name: "Käse", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Sahne", einheit: "ml", faktor: 100 },
|
||||||
|
{ gruppe: "Special Gewürze" },
|
||||||
|
{ name: "Knoblauch", einheit: " Zehe/n", faktor: 1 },
|
||||||
|
{ name: "Chili", einheit: " Scoville", faktor: 10_000},
|
||||||
|
{ gruppe: "Sonstiges" },
|
||||||
|
{ name: "Champignons", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Eier", einheit: "", faktor: 1 },
|
||||||
|
{ name: "Zwiebeln", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Tomaten stückig", einheit: "g", faktor: 200 },
|
||||||
|
{ name: "Meeresfrüchte" },
|
||||||
|
{ name: "Rucola", einheit: "g", faktor: 150 },
|
||||||
|
{ name: "Erbsen", einheit: "g", faktor: 100 },
|
||||||
|
{ name: "Spargel", einheit: " Stange/n", faktor: 1 },
|
||||||
|
{ name: "Oliven", einheit: "g", faktor: 50 },
|
||||||
|
{ name: "Kopfsalat", einheit: " Blätter", faktor: 1 },
|
||||||
|
],
|
||||||
|
filter: "",
|
||||||
|
init() {
|
||||||
|
this.zutaten.forEach((z, index) => {
|
||||||
|
if (z.name) {
|
||||||
|
z.menge = 0;
|
||||||
|
z.id = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.filter = "";
|
||||||
|
for (let index = 0; index < this.zutaten.length; index++) {
|
||||||
|
const zutat = this.zutaten[index];
|
||||||
|
if (zutat.menge && zutat.menge > 0) {
|
||||||
|
zutat.menge = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get zutaten_filter() {
|
||||||
|
if (this.filter.length == 0) {
|
||||||
|
return this.zutaten;
|
||||||
|
}
|
||||||
|
let filtered = this.zutaten.filter((z) => {
|
||||||
|
return (
|
||||||
|
z.gruppe ||
|
||||||
|
(z.name && z.name.toLowerCase().includes(this.filter.toLowerCase()))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return filtered;
|
||||||
|
},
|
||||||
|
menge(delta, id) {
|
||||||
|
let zutat = this.zutaten.find((z) => z.id == id);
|
||||||
|
zutat.menge = Math.max(0, Math.min(zutat.menge + delta, 10));
|
||||||
|
},
|
||||||
|
get rezept() {
|
||||||
|
let ausgabe = [];
|
||||||
|
for (let i = 0; i < this.zutaten.length; i++) {
|
||||||
|
const zutat = this.zutaten[i];
|
||||||
|
if (zutat.menge > 0 && zutat.einheit) {
|
||||||
|
let faktor = zutat.faktor ?? 1;
|
||||||
|
let runde = zutat.runde ?? 1;
|
||||||
|
ausgabe.push(
|
||||||
|
`${Math.round(zutat.menge * faktor * runde) / runde}${zutat.einheit} ${zutat.name}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ausgabe;
|
||||||
|
},
|
||||||
|
get rezeptname() {
|
||||||
|
let zutaten = this.zutaten.reduce(
|
||||||
|
(a, v) => ({ ...a, [v.name]: v.menge }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
let gesamt = Object.values(zutaten).reduce((a, b) => {
|
||||||
|
if (b) {
|
||||||
|
return a + b;
|
||||||
|
} else {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
if (zutaten["Salatgurke"] >= 5 && zutaten["Senf"] >= 6 && gesamt < 15) {
|
||||||
|
return "Senfgurken";
|
||||||
|
} else if (
|
||||||
|
zutaten["Hackfleisch"] >= 2 &&
|
||||||
|
zutaten["Nudeln"] >= 3 &&
|
||||||
|
zutaten["Tomaten stückig"] >= 2
|
||||||
|
) {
|
||||||
|
return "Spaghetti Bolognese";
|
||||||
|
} else if (
|
||||||
|
zutaten["Ketchup"] >= 3 &&
|
||||||
|
zutaten["Mayonnaise"] >= 3 &&
|
||||||
|
zutaten["Kartoffeln"] >= 1
|
||||||
|
) {
|
||||||
|
return "Pommes Rot-Weiß";
|
||||||
|
}
|
||||||
|
return "Kartoffelsalat";
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
Alpine.start();
|
||||||
115
style.css
Normal file
115
style.css
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.window {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rem;
|
||||||
|
margin: 1rem;
|
||||||
|
margin-top: 0;
|
||||||
|
@media (max-width: 650px) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.recipe {
|
||||||
|
min-width: 15rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
position: sticky;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
align-self: flex-start;
|
||||||
|
background: white;
|
||||||
|
top: 6rem;
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.actionbar {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: 1pt lightgray solid;
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
.grid {
|
||||||
|
width: min(100%, 806px);
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
|
||||||
|
.grid-element {
|
||||||
|
min-height: 1rem;
|
||||||
|
& > .zutat {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
box-shadow: 3px 3px 2px 0px rgba(0, 0, 0, 0.2);
|
||||||
|
border: 1pt solid silver;
|
||||||
|
border-bottom: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexbox {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: #666;
|
||||||
|
.bold {
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.grid-element:has(.heading) {
|
||||||
|
grid-column: -1/1;
|
||||||
|
border-bottom: 1pt solid hsl(0, 0%, 80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: transparent;
|
||||||
|
outline: 2pt solid rgba(0, 0, 0, 0.2);
|
||||||
|
border: 1pt solid silver;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
&.plus,
|
||||||
|
&.minus {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
&.plus {
|
||||||
|
outline-color: oklch(66.442% 0.22551 142.585 / 0.3);
|
||||||
|
}
|
||||||
|
&.minus {
|
||||||
|
outline-color: oklch(66.442% 0.22551 41.6 / 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:hover {
|
||||||
|
transition: 200ms background-color ease;
|
||||||
|
outline-offset: -1pt;
|
||||||
|
background-color: rgba(0, 0, 0, 0.025);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
max-width: 300px;
|
||||||
|
padding: .2rem;
|
||||||
|
/* border: 1pt solid silver; */
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user