diff --git a/js/views/RecipeDetailV2.js b/js/views/RecipeDetailV2.js index 03dc89f..b4af620 100644 --- a/js/views/RecipeDetailV2.js +++ b/js/views/RecipeDetailV2.js @@ -1,5 +1,4 @@ import { RECIPES, INGREDIENTS, PRODUCTS } from '../data/catalog.js?v=8'; -import { MEAL_SLOTS } from '../planner/mealSlots.js'; import { createIngredientCardController, getIngredientCardHTML } from '../ui/ingredientCard.js?v=20260410-107'; function escapeHtml(s) { @@ -10,7 +9,6 @@ function escapeHtml(s) { .replace(/"/g, '"'); } -const slotLabelMap = Object.fromEntries(MEAL_SLOTS.map((s) => [s.id, s.label])); const RD_THEME = Object.freeze({ surface: '#393937', surfaceSoft: '#2f2f2d', @@ -31,72 +29,42 @@ function forceBgBorder(bg, border) { return `${forceBg(bg)} border:1px solid ${border} !important;`; } -function renderTagChip(label) { - return `${escapeHtml(label)}`; -} - -function setTabButtonState(btn, active) { - if (!btn) return; - btn.style.color = active ? RD_THEME.textPrimary : RD_THEME.textMuted; - btn.style.borderBottomColor = active ? RD_THEME.borderStrong : 'transparent'; - btn.style.fontWeight = active ? '600' : '500'; -} - export function getRecipeDetailHTML() { return ` - -
+
- -
-
- -
- -
+
+
+ +
+ +
-
-
-
-

-
-
-
-
- - +
+
+
+

-
-
- - -
+
+

Składniki

+
+
-
-
- +
+
+

Kroki

+
+
@@ -116,6 +84,7 @@ let currentAddedIngredients = []; let currentProductSelections = {}; let expandedAlternatives = new Set(); let ingredientCard = null; +let resetScrollState = null; function isPlannedMode() { return currentMode === 'planned'; @@ -225,31 +194,11 @@ function populateDetail(recipeId, options = {}) { heroLabel.textContent = `Zdjęcie: ${recipe.title}`; } document.getElementById('rd-title').textContent = recipe.title; - document.getElementById('rd-time').textContent = `${recipe.minutes} min`; document.getElementById('rd-add-to-planner-btn')?.classList.toggle('hidden', isPlannedMode()); - const tagsHtml = []; - for (const slotId of recipe.allowedSlots) { - const label = slotLabelMap[slotId]; - if (label) tagsHtml.push(renderTagChip(label)); - } - for (const tag of (recipe.tags || [])) { - tagsHtml.push(renderTagChip(tag)); - } - document.getElementById('rd-tags').innerHTML = tagsHtml.join(''); - renderIngredients(recipe); renderSteps(recipe); - - const tabBtns = document.querySelectorAll('.rd-tab-btn'); - const tabs = document.querySelectorAll('.rd-tab-content'); - tabBtns.forEach((b) => { - setTabButtonState(b, false); - }); - setTabButtonState(tabBtns[0], true); - tabs.forEach((t) => { t.classList.add('hidden'); t.classList.remove('block'); }); - document.getElementById('rd-tab-ingredients')?.classList.remove('hidden'); - document.getElementById('rd-tab-ingredients')?.classList.add('block'); + resetScrollState?.(); } /* ── helpers ───────────────────────────────────────────── */ @@ -523,9 +472,9 @@ function renderSteps(recipe) { container.innerHTML = `
${steps.map((step, i) => ` -
-
${i + 1}
-

${escapeHtml(step)}

+
+
${i + 1}.
+

${escapeHtml(step)}

`).join('')}
`; } @@ -536,37 +485,81 @@ export function setupRecipeDetail() { ingredientCard = createIngredientCardController({ idBase: 'rd-ing-card', defaultSourceNote: 'Z planera' }); ingredientCard.bind(); - document.querySelectorAll('.rd-tab-btn').forEach((btn) => { - btn.addEventListener('click', () => { - const tabId = btn.dataset.rdTab; - document.querySelectorAll('.rd-tab-content').forEach((el) => { - el.classList.add('hidden'); - el.classList.remove('block'); - }); - const target = document.getElementById(`rd-tab-${tabId}`); - if (target) { - target.classList.remove('hidden'); - target.classList.add('block'); - target.parentElement.scrollTop = 0; - } + /* ── collapsing hero on scroll ─────────────── */ - document.querySelectorAll('.rd-tab-btn').forEach((b) => { - setTabButtonState(b, false); - }); - setTabButtonState(btn, true); - }); - }); + const HERO_MAX = 236; + const scrollContainer = document.getElementById('rd-scroll-container'); + const hero = document.getElementById('rd-hero'); + const heroImg = document.getElementById('rd-hero-img'); + const topActionButtons = [ + document.getElementById('rd-back-btn'), + document.getElementById('rd-add-to-planner-btn'), + ].filter(Boolean); - /* ── tab-bar scroll shadow ─────────────────── */ - - const scrollContainer = document.querySelector('#recipe-detail-view .overflow-y-auto'); - const tabBar = document.getElementById('rd-tab-bar'); - if (scrollContainer && tabBar) { - scrollContainer.addEventListener('scroll', () => { - tabBar.classList.toggle('rd-scrolled', scrollContainer.scrollTop > 2); + function syncTopActionButtons(progress) { + const shadowY = 3 + (progress * 3); + const shadowBlur = 8 + (progress * 6); + const shadowAlpha = 0.28 + (progress * 0.16); + const backgroundAlpha = 0.93 + (progress * 0.05); + topActionButtons.forEach((button) => { + const isRoundButton = button.id === 'rd-back-btn'; + const effectiveShadowY = isRoundButton ? shadowY + 1 : shadowY; + const effectiveShadowBlur = isRoundButton ? shadowBlur + 1 : shadowBlur; + const effectiveShadowAlpha = isRoundButton ? shadowAlpha + 0.05 : shadowAlpha; + button.style.boxShadow = `0 ${effectiveShadowY}px ${effectiveShadowBlur}px rgba(0,0,0,${effectiveShadowAlpha})`; + button.style.background = `rgba(57,57,55,${backgroundAlpha})`; }); } + if (scrollContainer && hero) { + let ticking = false; + let lastTouchY = null; + scrollContainer.addEventListener('scroll', () => { + if (ticking) return; + ticking = true; + requestAnimationFrame(() => { + const scrollY = Math.max(0, scrollContainer.scrollTop); + const collapse = Math.min(scrollY, HERO_MAX); + const progress = collapse / HERO_MAX; + hero.style.height = `${HERO_MAX - collapse}px`; + hero.style.opacity = String(Math.max(0, 1 - collapse / HERO_MAX)); + syncTopActionButtons(progress); + if (heroImg) { + heroImg.style.transform = `translateY(${collapse * 0.4}px) scale(${1 + collapse * 0.001})`; + } + ticking = false; + }); + }); + + scrollContainer.addEventListener('touchstart', (e) => { + lastTouchY = e.touches[0]?.clientY ?? null; + }, { passive: true }); + + scrollContainer.addEventListener('touchmove', (e) => { + const touchY = e.touches[0]?.clientY; + if (touchY == null) return; + if (scrollContainer.scrollTop <= 0 && lastTouchY != null && touchY > lastTouchY) { + e.preventDefault(); + } + lastTouchY = touchY; + }, { passive: false }); + + scrollContainer.addEventListener('touchend', () => { + lastTouchY = null; + }, { passive: true }); + + scrollContainer.addEventListener('touchcancel', () => { + lastTouchY = null; + }, { passive: true }); + } + + resetScrollState = () => { + if (scrollContainer) scrollContainer.scrollTop = 0; + if (hero) { hero.style.height = `${HERO_MAX}px`; hero.style.opacity = '1'; } + syncTopActionButtons(0); + if (heroImg) heroImg.style.transform = ''; + }; + /* ── planner — delegate to MealPlanEditor ─────── */ document.getElementById('rd-add-to-planner-btn')?.addEventListener('click', () => {