Твои любимые блюда в исполнении
Готовим из того, что есть Выберите блюдо, впишите продукты — и сайт покажет, сколько получится и что нужно докупить
Выбрать блюдо Ничего не пропадёт, ничего не на глаз Вы открываете рецепт, отмечаете, сколько продуктов уже есть дома, а калькулятор пересчитывает порции, список покупок и примерную стоимость докупки.
Выберите блюдо Каждая карточка открывает рецепт, таблицу ингредиентов и расчёт продуктов под ваши запасы.
Как это работает Шаг 1 Выберите блюдо Шаг 2 Впишите продукты Шаг 3 Получите расчёт Учебный пример
Такой сайт вы соберёте сами Это пример с практикума «Мастерская твоего первого сайта» — чтобы показать: сайт — это не только дизайн, а маленькая история, которую можно собрать своими руками даже в самом начале пути.
Базовый рецепт на ' + r.portions + ' порции
' + r.title + ' ' + r.description + '
Приготовление ' + r.steps.map(function(s) { return '' + s + ' '; }).join("") + ' Калькулятор продуктов Продукт Нужно Есть дома Цена ' + ingredientRows(r) + '
Для граммов цена считается за 1 кг, для миллилитров — за 1 л, для штук — за 1 шт. Незаполненные продукты не ограничивают количество порций.
Рассчитать
';
modal.classList.add("is-open");
modal.setAttribute("aria-hidden", "false");
document.documentElement.style.overflow = "hidden";
closeBtn.focus();
}
function closeModal() {
modal.classList.remove("is-open");
modal.setAttribute("aria-hidden", "true");
document.documentElement.style.overflow = "";
modalContent.innerHTML = "";
activeRecipe = null;
}
function readNum(attr, idx) {
var inp = modalContent.querySelector('[' + attr + '="' + idx + '"]');
if (!inp || inp.value === "") return null;
var v = Number(String(inp.value).replace(",", "."));
return Number.isFinite(v) && v >= 0 ? v : null;
}
function plural(n, f) { var m = Math.abs(n) % 100, m1 = m % 10; return m > 10 && m < 20 ? f[2] : m1 > 1 && m1 < 5 ? f[1] : m1 === 1 ? f[0] : f[2]; }
function calculate() {
if (!activeRecipe) return;
var r = activeRecipe, minC = Infinity, limiter = "базовый рецепт", hasInput = false;
r.ingredients.forEach(function(i, idx) { var h = readNum("data-have", idx); if (h !== null) { hasInput = true; var c = h / i[1]; if (c < minC) { minC = c; limiter = i[0].toLowerCase(); } } });
var exact = hasInput ? r.portions * minC : r.portions, whole = hasInput ? Math.floor(exact) : r.portions, enough = !hasInput || whole >= 1, target = hasInput ? Math.max(1, whole) : r.portions, factor = target / r.portions, totalCost = 0, hasMissing = false, hasUnpriced = false;
var list = r.ingredients.map(function(i, idx) { var needed = i[1] * factor, have = readNum("data-have", idx), price = readNum("data-price", idx), available = have === null ? 0 : have, missing = Math.max(0, needed - available), cost = null; if (missing > 0) hasMissing = true; if (missing > 0 && price !== null) { cost = i[2] === "pcs" ? missing * price : missing / 1000 * price; totalCost += cost; } if (missing > 0 && price === null) hasUnpriced = true; var status = missing <= 0 ? "✓ хватает" : "докупить " + fmt(missing, i[2]) + (cost !== null ? " · " + money(cost) : ""); return '
' + i[0] + ' нужно ' + fmt(needed, i[2]) + ' · ' + (have === null ? "не указано" : "есть " + fmt(available, i[2])) + ' · ' + status + ' '; }).join("");
var portionText = enough ? target + " " + plural(target, ["порция", "порции", "порций"]) : "Не хватает даже на одну порцию";
var shoppingTitle = hasInput && !enough ? "Список покупок для 1 порции" : "Список покупок";
var costText = hasMissing ? (totalCost > 0 ? (hasUnpriced ? "от " + money(totalCost) : money(totalCost)) : "без расчёта цены") : "0 ₽";
var res = modalContent.querySelector("[data-result]");
res.innerHTML = '
Получится ' + portionText + '
Ограничивает ' + (hasInput ? limiter : "Базовый рецепт") + '
Докупка примерно ' + costText + '
' + shoppingTitle + ' ';
res.classList.add("is-visible");
}
renderCards();
cardsRoot.addEventListener("click", function(e) { var c = e.target.closest("[data-recipe]"); if (c) openRecipe(c.getAttribute("data-recipe")); });
modal.addEventListener("click", function(e) { if (e.target === modal) closeModal(); if (e.target.closest("[data-calc]")) calculate(); });
closeBtn.addEventListener("click", closeModal);
document.addEventListener("keydown", function(e) { if (e.key === "Escape" && modal.classList.contains("is-open")) closeModal(); });
})();