diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..6f3a2913e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/bilder/carbonara-pic.jpg b/bilder/carbonara-pic.jpg new file mode 100644 index 000000000..766e54a76 Binary files /dev/null and b/bilder/carbonara-pic.jpg differ diff --git a/bilder/chicken.webp b/bilder/chicken.webp new file mode 100644 index 000000000..4c8031c6c Binary files /dev/null and b/bilder/chicken.webp differ diff --git a/index.css b/index.css new file mode 100644 index 000000000..2751d3161 --- /dev/null +++ b/index.css @@ -0,0 +1,438 @@ +*{ + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --first-color: #eaf6ff; + --second-color: #bffaed; + --third-color: #FFECEA; + --forth-color: #082bee; + --fifth-color: #c2e3fd; + --hover-1: #5dd4bb; + --hover-2: #f8cbc6; + --hover-3: #01199e; + --border-radius: 12px; + --transition: 0.3s ease; + +} + +body { + background-color: var(--fifth-color); + color: var(--text-dark); + font-family: "futura", sans-serif; + width: 100vw; + margin: 0; + padding: 0; +} + +h1 { + font: bold 4rem "futura", sans-serif; + color: var(--primary); + text-align: center; +} + +h4 { + font: bold 1.5rem "futura", sans-serif; + color: var(--text-dark); +} + +h5 { + font: bold 1rem "futura", sans-serif; + color: var(--text-light); + margin: 0; +} + +i { + color: var(--text-light); + cursor: pointer; + padding: 0 .25rem; +} + +ul { + padding: .25rem; + margin: 0; + text-align: left; +} + +#mobileDropdown { + margin-bottom: 2rem; + column-gap: 2rem; +} + +.filtersort-larger { + display: none; +} + +.filtersort-mobile { + display: flex; + flex-direction: column; + align-items: center; +} + +.filter-content, +.sort-content { + display: none; + position: absolute; + z-index: 2; + width: 100vw; + min-width: 150px; + text-align: center; + top: 100%; + box-sizing: border-box; +} + +.filter-content { + background-color: var(--primary); +} + +.sort-content { + background-color: var(--secondary); +} + +.show { + display: block; +} + +/* Filter buttons */ +.dropbtn { + font: 1.25rem "futura", sans-serif; + color: var(--primary); + width: 100dvw; + border: none; + padding: 1rem .5rem; + cursor: pointer; +} + +#sortDropdown .sort-button, +#filterDropdown .filter-button { + font: 1rem "futura", sans-serif; + width: 100dvw; + border: none; + cursor: pointer; + margin-bottom: .5rem; +} + +#sortDropdown .sort-button { + color: var(--text-light); + background-color: var(--secondary); +} + +#filterDropdown .filter-button { + color: var(--text-light); + background-color: var(--primary); +} + +.filter, +.sort { + position: relative; +} + +.filter.active>button { + color: var(--text-light); + background-color: var(--primary); + border: 2px solid var(--primary); + z-index: 2; +} + +.sort.active>button { + color: var(--text-light); + background-color: var(--secondary); + border: 2px solid var(--secondary); + z-index: 2; +} + +.filter>button { + background-color: var(--select-dark); + color: var(--primary); + cursor: pointer; +} + +.sort>button { + background-color: var(--select-light); + color: var(--primary); + cursor: pointer; +} + +.search-input { + display: flex; + position: relative; + justify-content: center; + width: 100%; + height: 66.5px; + box-sizing: border-box; + white-space: nowrap; + padding: 1rem .5rem; + font: 1, 25rem "futura", sans-serif; + color: var(--primary); + text-align: center; + border: none; +} + +::placeholder { + text-align: center; +} + +/* Random button */ +#randomRecipeButton, +#randomMobile { + display: block; + font: 1.25rem "futura", sans-serif; + width: 100vw; + border: none; + padding: 1rem .5rem; + cursor: pointer; + background-color: var(--primary); + color: var(--text-light); +} + +/* Recipe cards */ +.recipe-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-content: start; + gap: .5rem; + margin-bottom: 1rem; + width: auto; +} + +.recipe-card { + display: flex; + flex-direction: column; + background-color: #ffffff; + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + border-radius: 1rem; + width: 20rem; + height: 100%; + padding: 1rem 1rem 1.5rem 1rem; + z-index: 1; + box-sizing: border-box; + text-align: start; + margin: .5rem; +} + +.sort li { + text-align: center; + color: var(--text-light); +} + +.ingredients-list { + display: flex; + flex-wrap: wrap; + flex-direction: column; + align-items: flex-start; + padding: 0; +} + +.ingredients-list li { + display: flex; + flex-direction: column; + text-align: start; + padding: 0; + margin: 0px 0px .5rem .5rem; + color: var(--text-dark); +} + +span { + width: 16rem; + height: 2px; + background-color: var(--border-light); + justify-self: center; +} + +img { + object-fit: cover; + max-width: 18rem; + max-height: 12.5rem; + border-radius: 1rem; + align-self: center; + box-sizing: border-box; +} + +/* Footer */ +.footer-parent { + display: flex; + flex-direction: row; + justify-content: space-evenly; + background-color: var(--primary); + padding: 1rem 0; + align-items: center; + align-content: center; + max-height: fit-content; +} + +.footer-right, +.footer-left { + display: inline-flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + color: var(--background-light); + font: 1rem "futura", sans-serif; +} + +.footer-right li a { + text-decoration: none; + display: block; + color: var(--text-light); + text-align: center; +} + +li { + list-style-type: none; +} + +@media (min-width: 820px) { + .filtersort-mobile { + display: none; + } + + + + h1 { + text-align: left; + padding-left: 3rem; + } + + button:hover { + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + } + + .green { + display: flex; + flex-wrap: wrap; + justify-content: left; + } + + .filter .green>button:focus { + color: var(--text-light); + background-color: var(--primary); + border: 2px solid var(--primary); + } + + .sort .selectors>button:focus { + color: var(--select-light); + background-color: var(--secondary); + border: 2px solid var(--secondary); + } + + button { + border-radius: 4rem; + border: 1px solid var(--background-light); + cursor: pointer; + padding: .5rem 1rem; + } + + .filter .green>button { + background-color: var(--select-dark); + color: var(--primary); + margin: .25rem; + } + + .sort .selectors>button { + background-color: var(--select-light); + color: var(--primary); + margin: .25rem; + } + + .sort { + justify-content: start; + width: 35vh; + } + + .filter { + justify-content: start; + width: 35vw; + } + + .filtersort-larger { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0 3rem; + } + + .search-container { + display: flex; + background-color: var(--background-light); + border: none; + height: auto; + width: auto; + } + + .search-input:hover { + border: 2px solid var(--border-dark); + border-radius: 3rem; + height: 2rem; + } + + .search-input { + width: 35vh; + border: 2px solid var(--background-light); + border-radius: 4rem; + outline: none; + text-indent: .5rem; + font: 1rem "futura", sans-serif; + height: 2rem; + box-shadow: 0px 0px 2.5rem var(--shadow-light); + padding: 0; + margin: 0; + } + + .right-side-nav { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + align-content: center; + padding: 2rem 1rem 2rem 0; + gap: 1rem; + } + + #randomRecipeButton { + font: 1rem arial, sans-serif; + width: 35vh; + border-radius: 3rem; + border: 1px solid var(--primary); + padding: .5rem 1rem; + max-height: 2rem; + box-shadow: 0px 0px 2.5rem var(--shadow-light); + background-color: var(--primary); + color: var(--text-light); + cursor: pointer; + height: 2rem; + } + + .recipe-container { + align-content: start; + justify-content: space-evenly; + gap: 1rem; + padding: 1rem; + align-self: center; + } + + .recipe-card { + width: 300px; + border: 2px solid var(--background-light); + margin-bottom: 0; + align-self: flex-start; + } + + .recipe-card:hover { + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + border-radius: 1rem; + } + + .footer-left, + .footer-right { + gap: 2rem; + font: 2rem "futura", sans-serif; + color: var(--text-light); + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..347dc4fcf --- /dev/null +++ b/index.html @@ -0,0 +1,66 @@ + + + + + + Document + + + + + +
+

Recipe Library

+ + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 000000000..23976dd3a --- /dev/null +++ b/script.js @@ -0,0 +1,231 @@ +const BASE_URL = `https://api.spoonacular.com/recipes/`; +const API_KEY = `9e72cc93680145b5a3d90f62a051dd28`; +const fixedCuisines = [ + "Asian", "American","Italian","Middle Eastern", +]; + +const URL = `${BASE_URL}complexSearch?apiKey=${API_KEY}&number=10&cuisine=${fixedCuisines.join(',')}&fillIngredients=true&addRecipeInformation=true&sort=random`; + +const recipeContainer = document.getElementById("recipe-container"); +const filterDropdown = document.getElementById("filterDropdown"); +const buttonContainer = document.getElementById("filterButtonsContainer"); +const missingRecipe = document.getElementById("missingRecipe"); +const dropdowns = document.querySelectorAll(".dropdown"); + +// Function to select a random recipe +const getRandomRecipe = (recipes) => { + const randomIndex = Math.floor(Math.random() * recipes.length); + return recipes[randomIndex]; +}; + +// Fetch recipes +const receiveRecipeData = () => { + fetch(URL) + .then(response => { + if (!response.ok) throw new Error("Something whent wrong"); + return response.json(); + }) + .then(data => { + const recipes = data.results; + localStorage.setItem("recipes", JSON.stringify(recipes)); + initializePage(recipes); + }) + .catch(error => { + alert("There is an issue with retrieving your recipes,\n please try again or continue with a new search word.\n\n " + error); + + const storedRecipes = localStorage.getItem("recipes"); + if (storedRecipes) { + const recipes = JSON.parse(storedRecipes); + initializePage(recipes); + } + + }); + +} +receiveRecipeData() + + +const initializePage = (recipes) => { + generateDropdown(filterDropdown, recipes, "filter"); + generateButtons(buttonContainer, recipes, "filter"); + generateDropdown(document.getElementById("sortDropdown"), recipes, "sort"); + generateButtons(document.getElementById("sortButtons"), recipes, "sort"); + document.querySelectorAll("#search, #searchMobile").forEach(input => { + input.addEventListener("input", () => FindRecipe(recipes)); + }); + document.querySelectorAll("#randomRecipeButton, #randomMobile").forEach(button => { + button.addEventListener("click", () => { + console.log("Random recipe button clicked"); + const randomRecipe = getRandomRecipe(recipes); + fillRecipeCards([randomRecipe]); + }); + }); + + fillRecipeCards(recipes); +} + +// Get unique cuisine filter values +const getUniqueFilter = (recipes) => { + const uniqueCuisines = new Set(); + recipes.forEach(recipe => { + (recipe.cuisines || []).forEach(cuisine => uniqueCuisines.add(cuisine)); + }); + return [...uniqueCuisines]; +}; + +// Generate buttons +const generateButtons = (container, recipes, type) => { + if (!container) return; + + const items = type === "filter" ? getUniqueFilter(recipes) : ["Servings", "Popularity", "Time"]; + + const buttonClass = type === "filter" ? "filter-button" : "sort-button"; + + const buttonText = type === "filter" ? "All Cuisines" : "Reset"; + + container.innerHTML = ` + + + ${items + .map( + (item) => ` + + +`).join("")} + `; + + document.querySelectorAll(`.${buttonClass}`).forEach(button => { + button.addEventListener("click", (event) => type === "filter" ? filterRecipe(event, recipes) : sortRecipes(event, recipes)); + }); +}; + +// Generate dropdown +const generateDropdown = (container, recipes, type) => { + if (!container) return; + + const items = type === "filter" ? getUniqueFilter(recipes) : ["Servings", "Popularity", "Time"]; + const buttonClass = type === "filter" ? "filter-button" : "sort-button"; + const buttonText = type === "filter" ? "All" : "Reset"; + + container.innerHTML = ` +
  • + ${items.map(item => `
  • `).join("")} + `; + + document.querySelectorAll(`.${buttonClass}`).forEach(option => { + option.addEventListener("click", (event) => type === "filter" ? filterRecipe(event, recipes) : sortRecipes(event, recipes)); + }); +}; + +// Render recipe cards +const fillRecipeCards = (recipes) => { + if (!recipeContainer) return; + + recipeContainer.innerHTML = ""; + + recipes.forEach(recipe => { + const ingredientList = (recipe.extendedIngredients || []) + .map(ingredient => `
  • ${ingredient.original}
  • `) + .join(""); + + const cleanDietsText = (recipe.diets || []).map(diet => diet.charAt(0).toUpperCase() + diet.slice(1)).join(", "); + const cleancuisinesText = (recipe.cuisines || []).map(cuisine => cuisine.charAt(0).toUpperCase() + cuisine.slice(1)).join(", "); + + const makeRecipeCard = document.createElement("div"); + makeRecipeCard.classList.add("recipe-card"); + + makeRecipeCard.innerHTML = ` + ${recipe.title} +

    ${recipe.title}

    + +

    Cuisine: ${cleancuisinesText}

    +

    Diet: ${cleanDietsText}

    + +

    Ready in: ${recipe.readyInMinutes} minutes

    +

    Servings: ${recipe.servings}

    + +

    Ingredients:

    + + Get Recipe + `; + + recipeContainer.appendChild(makeRecipeCard); + }); +}; + +// Filter function for cuisine +const filterRecipe = (event, recipes) => { + event.preventDefault(); + const filterValue = event.target.value; + + if (filterValue === "all") { + fillRecipeCards(recipes); + } else { + const filteredRecipes = recipes.filter(recipe => recipe.cuisines.includes(filterValue)); + fillRecipeCards(filteredRecipes); + } +}; + +// Sorting function +const sortRecipes = (event, recipes) => { + event.preventDefault(); + const sortValue = event.target.value; + + let sortedRecipes = [...recipes]; + + if (sortValue === "Servings") { + sortedRecipes.sort((a, b) => (a.servings || 0) - (b.servings || 0)); + } else if (sortValue === "Popularity") { + sortedRecipes.sort((a, b) => (b.aggregateLikes || 0) - (a.aggregateLikes || 0)); + } else if (sortValue === "Time") { + sortedRecipes.sort((a, b) => (a.readyInMinutes || 0) - (b.readyInMinutes || 0)); + } + + fillRecipeCards(sortedRecipes); +}; + + +document.addEventListener("click", (event) => { + dropdowns.forEach(dropdown => { + const menu = dropdown.querySelector(".filter-content, .sort-content"); + if (!menu) return; + + if (dropdown.contains(event.target)) { + event.stopPropagation(); + menu.classList.toggle("show"); + dropdown.classList.toggle("active"); + } else { + menu.classList.remove("show"); + dropdown.classList.remove("active"); + } + }); +}); + +// search function +const FindRecipe = (recipes) => { + if (!recipes) { + return; + } + let query = document.getElementById("search").value.toLowerCase() || document.getElementById("searchMobile").value.toLowerCase(); + + const filteredRecipes = recipes.filter(recipe => + recipe.title.toLowerCase().includes(query) || + recipe.cuisines.some(cuisine => cuisine.toLowerCase().includes(query)) || + recipe.diets.some(diet => diet.toLowerCase().includes(query)) + ); + + if (query === "") { + fillRecipeCards(recipes); + } else if (filteredRecipes.length === 0) { + recipeContainer.innerHTML = `

    No matching recipes found, try something else?

    `; + } else { + fillRecipeCards(filteredRecipes); + } +} \ No newline at end of file diff --git a/spoonacular API.docx b/spoonacular API.docx new file mode 100644 index 000000000..1258d8d86 Binary files /dev/null and b/spoonacular API.docx differ diff --git a/style.css b/style.css new file mode 100644 index 000000000..3f9a6c423 --- /dev/null +++ b/style.css @@ -0,0 +1,408 @@ +*{ + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --background-light: #f2f8fd; + --select-green: #a7f8e7; + --select-pink: #ffe0e0; + --primary: #0a26ca; + --second-pink: #ffc4c4; + --second-green: #44f8d1; + --shadow-light: rgba(212, 212, 212, 0.671); + --border-dark: #1f2521; + --text-underline: #56af8a; + --text-select: #e9ffe9; + --text-dark: #1a1818; + --find: #d0ff00; +} + + +body { + background-color: var(--background-light); + color: var(--text-dark); + font-family: "futura", sans-serif; + width: 100vw; + margin: 0; + padding: 0; +} + +h1 { + font: bold 4rem "futura", sans-serif; + color: var(--primary); + text-align: center; +} + +h4 { + font: bold 1.5rem "futura", sans-serif; + color: var(--text-dark); +} + +h5 { + font: bold 1rem "futura", sans-serif; + color: var(--text-select); + margin: 0; +} + +i { + color: var(--text-select); + cursor: pointer; + padding: 0 .25rem; +} + +ul { + padding: .25rem; + margin: 0; + text-align: left; +} + +#mobileDropdown { + margin-bottom: 2rem; + column-gap: 2rem; +} + +.filtersort-larger { + display: none; +} + +.filtersort-mobile { + display: flex; + flex-direction: column; + align-items: center; +} + +.filter-content, +.sort-content { + display: none; + position: absolute; + z-index: 2; + width: 100vw; + min-width: 150px; + text-align: center; + top: 100%; + box-sizing: border-box; +} + +.filter-content { + background-color: var(--select-green); +} + +.sort-content { + background-color: var(--select-pink); +} + +.show { + display: block; +} + +/* Filter buttons */ +.dropbtn { + font: 1.25rem "futura", sans-serif; + color: var(--find); + width: 100dvw; + border: none; + padding: 1rem .5rem; + cursor: pointer; +} + +#sortDropdown .sort-button, +#filterDropdown .filter-button { + font: 1rem "futura", sans-serif; + width: 100dvw; + border: none; + cursor: pointer; + margin-bottom: .5rem; +} + +/*color of dropdown mobile pink*/ +#sortDropdown .sort-button { + color: var(--primary); + background-color: var(--select-pink); +} +/*color of dropdown mobile green*/ +#filterDropdown .filter-button { + color: var(--primary); + background-color: var(--select-green); +} + +.filter, +.sort { + position: relative; +} +/*dropdown menu color*/ +.filter.active>button { + color: var(--primary); + background-color: var(--second-green); + border: 2px solid var(--second-green); + z-index: 2; +} + +.sort.active>button { + color: var(--primary); + background-color: var(--second-pink); + border: 2px solid var(--second-pink); + z-index: 2; +} + +.filter>button { + background-color: var(--select-green); + color: var(--primary); + cursor: pointer; +} + +.sort>button { + background-color: var(--second-pink); + color: var(--primary); + cursor: pointer; +} + +.search-input { + display: flex; + position: relative; + justify-content: center; + width: 100%; + height: 66.5px; + box-sizing: border-box; + white-space: nowrap; + padding: 1rem .5rem; + font: 1, 25rem "futura", sans-serif; + color: var(--primary); + text-align: center; + border: none; +} + +::placeholder { + text-align: center; +} +/* */ + +/* Random button mobile*/ +#randomRecipeButton, +#randomMobile { + display: block; + font: 1.25rem "futura", sans-serif; + width: 100vw; + border: none; + padding: 1rem .5rem; + cursor: pointer; + background-color: var(--primary); + color: var(--text-select); +} + +/* Recipe cards */ +.recipe-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-content: start; + gap: .5rem; + margin-bottom: 1rem; + width: auto; +} + +.recipe-card { + display: flex; + flex-direction: column; + background-color: #ffffff; + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + border-radius: 1rem; + width: 20rem; + height: 100%; + padding: 1rem 1rem 1.5rem 1rem; + z-index: 1; + box-sizing: border-box; + text-align: start; + margin: .5rem; +} + +.sort li { + text-align: center; + color: var(--text-select); +} + +.ingredients-list { + display: flex; + flex-wrap: wrap; + flex-direction: column; + align-items: flex-start; + padding: 0; +} + +.ingredients-list li { + display: flex; + flex-direction: column; + text-align: start; + padding: 0; + margin: 0px 0px .5rem .5rem; + color: var(--text-dark); +} + +span { + width: 16rem; + height: 2px; + background-color: var(--text-underline); + justify-self: center; +} + +img { + object-fit: cover; + max-width: 18rem; + max-height: 12.5rem; + border-radius: 1rem; + align-self: center; + box-sizing: border-box; +} + + + + + +@media (min-width: 520px) { + .filtersort-mobile { + display: none; + } + + + + h1 { + text-align: left; + padding-left: 3rem; + } + + button:hover { + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + } + + .green { + display: flex; + flex-wrap: wrap; + justify-content: left; + } + + .filter .green>button:focus { + color: var(--primary); + background-color: var(--second-green); + border: 2px solid var(--primary); + } + + .sort .selectors>button:focus { + color: var(--primary); + background-color: var(--second-pink); + border: 2px solid var(--primary); + } + + button { + border-radius: 4rem; + border: 1px solid var(--background-light); + cursor: pointer; + padding: .5rem 1rem; + } + + .filter .green>button { + background-color: var(--select-green); + color: var(--primary); + margin: .25rem; + } + + .sort .selectors>button { + background-color: var(--select-pink); + color: var(--primary); + margin: .25rem; + } + + .sort { + justify-content: start; + width: 35vh; + } + + .filter { + justify-content: start; + width: 35vw; + } + + .filtersort-larger { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0 3rem; + } + + .search-container { + display: flex; + background-color: var(--background-light); + border: none; + height: auto; + width: auto; + } + + .search-input:hover { + border: 2px solid var(--border-dark); + border-radius: 3rem; + height: 2rem; + } + + .search-input { + width: 35vh; + border: 2px solid var(--background-light); + border-radius: 4rem; + outline: none; + text-indent: .5rem; + font: 1rem "futura", sans-serif; + height: 2rem; + box-shadow: 0px 0px 2.5rem var(--shadow-light); + padding: 0; + margin: 0; + } + + .right-side-nav { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + align-content: center; + padding: 2rem 1rem 2rem 0; + gap: 1rem; + } + + #randomRecipeButton { + font: 1rem arial, sans-serif; + width: 35vh; + border-radius: 3rem; + border: 1px solid var(--primary); + padding: .5rem 1rem; + max-height: 2rem; + box-shadow: 0px 0px 2.5rem var(--shadow-light); + background-color: var(--primary); + color: var(--text-select); + cursor: pointer; + height: 2rem; + } + + .recipe-container { + align-content: start; + justify-content: space-evenly; + gap: 1rem; + padding: 1rem; + align-self: center; + } + + .recipe-card { + width: 300px; + border: 2px solid var(--background-light); + margin-bottom: 0; + align-self: flex-start; + } + + .recipe-card:hover { + border: 2px solid var(--border-dark); + box-shadow: 0px 0px 2.5rem var(--shadow-light); + border-radius: 1rem; + } + + +} \ No newline at end of file diff --git a/text.md b/text.md new file mode 100644 index 000000000..655b13e0a --- /dev/null +++ b/text.md @@ -0,0 +1,5 @@ +https://api.spoonacular.com/recipes/ + +api key: 9e72cc93680145b5a3d90f62a051dd28' + +https://recipe-library-carina.netlify.app/ \ No newline at end of file