From dcad09f5ce30485581c6690450fa1530ad5f2a0d Mon Sep 17 00:00:00 2001 From: ulfrxdev Date: Thu, 19 Mar 2026 22:14:12 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + css/styles.css | 53 +++++++++++ index.html | 36 ++++++++ js/app.js | 45 ++++++++++ js/views/Filter.js | 61 +++++++++++++ js/views/RecipeDetail.js | 157 +++++++++++++++++++++++++++++++++ js/views/RecipeList.js | 185 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 538 insertions(+) create mode 100644 .gitignore create mode 100644 css/styles.css create mode 100644 index.html create mode 100644 js/app.js create mode 100644 js/views/Filter.js create mode 100644 js/views/RecipeDetail.js create mode 100644 js/views/RecipeList.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000..8272995 --- /dev/null +++ b/css/styles.css @@ -0,0 +1,53 @@ +/* Slider styling */ +input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius: 50%; + background: #111827; + cursor: pointer; + margin-top: -8px; +} +input[type=range]::-webkit-slider-runnable-track { + width: 100%; + height: 4px; + cursor: pointer; + background: #e5e7eb; + border-radius: 2px; +} + +/* View Transitions */ +.view-transition { + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; +} +.slide-in { + transform: translateX(0); + opacity: 1; +} +.slide-out { + transform: translateX(100%); + opacity: 0; + pointer-events: none; +} + +/* Ingredient Active States */ +.ingredient-active .check-box { + background-color: #111827; + border-color: #111827; +} +.ingredient-active .check-icon { + display: block; +} +.ingredient-active .ingredient-text { + text-decoration: line-through; + color: #9ca3af; +} + +/* Utilities */ +.no-scrollbar::-webkit-scrollbar { + display: none; +} +.no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..8a075ee --- /dev/null +++ b/index.html @@ -0,0 +1,36 @@ + + + + + + Recipe App - Modular + + + + + + +
+
+ + + + \ No newline at end of file diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..0ce03e6 --- /dev/null +++ b/js/app.js @@ -0,0 +1,45 @@ +import { getRecipeListHTML } from './views/RecipeList.js'; +import { getFilterHTML, setupFilter } from './views/Filter.js'; +import { getRecipeDetailHTML, setupRecipeDetail } from './views/RecipeDetail.js'; + +document.addEventListener('DOMContentLoaded', () => { + const appContainer = document.getElementById('app-container'); + + // Inject all views into the main container + appContainer.innerHTML = ` + ${getRecipeListHTML()} + ${getRecipeDetailHTML()} + ${getFilterHTML()} + `; + + // Initialize logic for the injected views + setupFilter(); + setupRecipeDetail(); +}); + +// --- GLOBAL NAVIGATION METHODS --- +window.openRecipeDetail = () => { + const view = document.getElementById('recipe-detail-view'); + // Swap Tailwind classes to slide IN + view.classList.remove('translate-x-full', 'opacity-0', 'pointer-events-none'); + view.classList.add('translate-x-0', 'opacity-100', 'pointer-events-auto'); +}; + +window.closeRecipeDetail = () => { + const view = document.getElementById('recipe-detail-view'); + // Swap Tailwind classes to slide OUT + view.classList.remove('translate-x-0', 'opacity-100', 'pointer-events-auto'); + view.classList.add('translate-x-full', 'opacity-0', 'pointer-events-none'); +}; + +window.openFilters = () => { + const fv = document.getElementById('filter-view'); + fv.classList.remove('hidden'); + fv.classList.add('flex'); +}; + +window.closeFilters = () => { + const fv = document.getElementById('filter-view'); + fv.classList.add('hidden'); + fv.classList.remove('flex'); +}; \ No newline at end of file diff --git a/js/views/Filter.js b/js/views/Filter.js new file mode 100644 index 0000000..1fd9fb2 --- /dev/null +++ b/js/views/Filter.js @@ -0,0 +1,61 @@ +export function getFilterHTML() { + return ` + + `; +} + +export function setupFilter() { + const timeSlider = document.getElementById('prep-time-slider'); + const timeDisplay = document.getElementById('time-display'); + + if(timeSlider) { + timeSlider.addEventListener('input', (e) => { + const val = e.target.value; + timeDisplay.textContent = val >= 120 ? '120+ min' : `${val} min`; + }); + } +} \ No newline at end of file diff --git a/js/views/RecipeDetail.js b/js/views/RecipeDetail.js new file mode 100644 index 0000000..3482329 --- /dev/null +++ b/js/views/RecipeDetail.js @@ -0,0 +1,157 @@ +export function getRecipeDetailHTML() { + return ` +
+ +
+ + +
+ +
+ Pancakes Hero Image +
+ +
+ +
+
+

Fluffy Pancakes

+
+ +
+ Breakfast + Vegetarian +
+ +
+
+
15 min
+
320 cal
+
+ +
+ +
+ 2 + +
+ +
+
+
+ +
+ + + +
+ +
+ +
+
    +
  • +
    + All-purpose flour + 1 cup +
  • +
  • +
    + Milk + 0.75 cup +
  • +
  • +
    + Eggs + 1 large +
  • +
+ + +
+ + + + + +
+
+
+ `; +} + +export function setupRecipeDetail() { + let currentServings = 2; + const defaultServings = 2; + + window.switchTab = (tabId, clickedBtn) => { + document.querySelectorAll('.tab-content').forEach(el => { + el.classList.remove('block'); + el.classList.add('hidden'); + }); + + const targetTab = document.getElementById(`tab-${tabId}`); + targetTab.classList.remove('hidden'); + targetTab.classList.add('block'); + targetTab.parentElement.scrollTop = 0; + + document.querySelectorAll('.tab-btn').forEach(btn => { + btn.classList.remove('text-gray-900', 'border-gray-900', 'font-semibold'); + btn.classList.add('text-gray-500', 'border-transparent', 'font-medium'); + }); + + clickedBtn.classList.remove('text-gray-500', 'border-transparent', 'font-medium'); + clickedBtn.classList.add('text-gray-900', 'border-gray-900', 'font-semibold'); + }; + + window.toggleIngredient = (element) => { + element.classList.toggle('ingredient-active'); + }; + + window.changeServings = (delta) => { + const newServings = currentServings + delta; + if (newServings < 1) return; + + currentServings = newServings; + document.getElementById('servings-count').innerText = currentServings; + + const ratio = currentServings / defaultServings; + document.querySelectorAll('.ingredient-amount').forEach(el => { + const baseAmount = parseFloat(el.getAttribute('data-base-amount')); + const unit = el.getAttribute('data-unit'); + + if (!isNaN(baseAmount)) { + let newAmount = baseAmount * ratio; + newAmount = Number.isInteger(newAmount) ? newAmount : parseFloat(newAmount.toFixed(2)); + el.innerText = `${newAmount} ${unit}`; + } + }); + }; +} \ No newline at end of file diff --git a/js/views/RecipeList.js b/js/views/RecipeList.js new file mode 100644 index 0000000..25df931 --- /dev/null +++ b/js/views/RecipeList.js @@ -0,0 +1,185 @@ +export function getRecipeListHTML() { + return ` +
+
+
+
+ +
+ +
+
+ +
+

24 Recipes Found

+
+ +
+
+ +
+
+ Pancakes +
+
+

Fluffy Pancakes

+

Classic breakfast pancakes

+
+
+
15m
+
320 cal
+
+
+ Breakfast +
+
+
+
+ +
+
+ Salad +
+
+

Chicken Salad

+

Healthy greens with grilled chicken

+
+
+
20m
+
250 cal
+
+
+ Lunch +
+
+
+
+ +
+
+ Pasta +
+
+

Tomato Basil Pasta

+

Rich garlic and tomato sauce

+
+
+
30m
+
450 cal
+
+
+ Dinner +
+
+
+
+ +
+
+ Smoothie +
+
+

Berry Smoothie

+

Mixed berries and yogurt power blend

+
+
+
5m
+
180 cal
+
+
+ Snack +
+
+
+
+ +
+
+ Avocado Toast +
+
+

Avocado Toast

+

Sourdough with smashed avocado

+
+
+
10m
+
220 cal
+
+
+ Breakfast +
+
+
+
+ +
+
+ Salmon +
+
+

Grilled Salmon

+

Fresh salmon with lemon butter

+
+
+
25m
+
380 cal
+
+
+ Dinner +
+
+
+
+ +
+
+ Tacos +
+
+

Beef Tacos

+

Spicy ground beef with fresh salsa

+
+
+
20m
+
410 cal
+
+
+ Dinner +
+
+
+
+ +
+
+ Oatmeal +
+
+

Oatmeal Bowl

+

Warm oats with honey and nuts

+
+
+
10m
+
210 cal
+
+
+ Breakfast +
+
+
+
+ +
+
+ +
+ + + + +
+
+ `; +} \ No newline at end of file