mirror of
https://github.com/Nezumi-2711/practice-js.git
synced 2026-09-22 05:32:05 +00:00
Refactoring code to MVC architecture
This commit is contained in:
Generated
+17
-1
@@ -10,11 +10,13 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"core-js": "^3.32.0",
|
||||
"fractional": "^1.0.0",
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-sass": "^2.9.3",
|
||||
"parcel": "^2.9.3"
|
||||
"parcel": "^2.9.3",
|
||||
"process": "^0.11.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -2592,6 +2594,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fractional": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fractional/-/fractional-1.0.0.tgz",
|
||||
"integrity": "sha512-4huGcVdAAr0ffcQ7wQY3dhAdZqKd6QgGOP4Kd7ioNoBAS7GdzolwnqgjH/jTzzCpg71oTLbOVwcOWOH+BmwqvA=="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@@ -3337,6 +3344,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-overlay": {
|
||||
"version": "6.0.9",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||
|
||||
+3
-1
@@ -10,10 +10,12 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-sass": "^2.9.3",
|
||||
"parcel": "^2.9.3"
|
||||
"parcel": "^2.9.3",
|
||||
"process": "^0.11.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.32.0",
|
||||
"fractional": "^1.0.0",
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
+11
-136
@@ -1,6 +1,8 @@
|
||||
import icons from '../img/icons.svg';
|
||||
import * as model from './model.js';
|
||||
import recipeView from './views/recipeView.js';
|
||||
|
||||
const recipeContainer = document.querySelector('.recipe');
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
const timeout = function (s) {
|
||||
return new Promise(function (_, reject) {
|
||||
@@ -14,150 +16,23 @@ const timeout = function (s) {
|
||||
|
||||
///////////////////////////////////////
|
||||
|
||||
const renderSpinner = function (parentEl) {
|
||||
const markup = `
|
||||
<div class="spinner">
|
||||
<svg>
|
||||
<use href="${icons}#icon-loader"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
parentEl.innerHTML = '';
|
||||
parentEl.insertAdjacentHTML('afterbegin', markup);
|
||||
};
|
||||
|
||||
const showRecipe = async function () {
|
||||
const controlRecipes = async function () {
|
||||
try {
|
||||
const id = window.location.hash.slice(1);
|
||||
|
||||
if (!id) return;
|
||||
recipeView.renderSpinner();
|
||||
|
||||
// 1)Loading recipe
|
||||
renderSpinner(recipeContainer);
|
||||
const res = await fetch(
|
||||
`https://forkify-api.herokuapp.com/api/v2/recipes/${id}`
|
||||
);
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) throw new Error(`${data.message} (${res.status})`);
|
||||
|
||||
let { recipe } = data.data;
|
||||
|
||||
recipe = {
|
||||
id: recipe.id,
|
||||
title: recipe.title,
|
||||
publisher: recipe.publisher,
|
||||
sourceUrl: recipe.source_url,
|
||||
image: recipe.image_url,
|
||||
servings: recipe.servings,
|
||||
cookingTime: recipe.cooking_time,
|
||||
ingredients: recipe.ingredients,
|
||||
};
|
||||
|
||||
console.log(recipe);
|
||||
await model.loadRecipe(id);
|
||||
|
||||
// 2) Rendering recipe
|
||||
const markup = `
|
||||
<figure class="recipe__fig">
|
||||
<img src="${recipe.image}" alt="${recipe.title}" class="recipe__img" />
|
||||
<h1 class="recipe__title">
|
||||
<span>${recipe.title}</span>
|
||||
</h1>
|
||||
</figure>
|
||||
|
||||
<div class="recipe__details">
|
||||
<div class="recipe__info">
|
||||
<svg class="recipe__info-icon">
|
||||
<use href="${icons}#icon-clock"></use>
|
||||
</svg>
|
||||
<span class="recipe__info-data recipe__info-data--minutes">${
|
||||
recipe.cookingTime
|
||||
}</span>
|
||||
<span class="recipe__info-text">minutes</span>
|
||||
</div>
|
||||
<div class="recipe__info">
|
||||
<svg class="recipe__info-icon">
|
||||
<use href="${icons}#icon-users"></use>
|
||||
</svg>
|
||||
<span class="recipe__info-data recipe__info-data--people">${
|
||||
recipe.servings
|
||||
}</span>
|
||||
<span class="recipe__info-text">servings</span>
|
||||
|
||||
<div class="recipe__info-buttons">
|
||||
<button class="btn--tiny btn--increase-servings">
|
||||
<svg>
|
||||
<use href="${icons}#icon-minus-circle"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn--tiny btn--increase-servings">
|
||||
<svg>
|
||||
<use href="${icons}#icon-plus-circle"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recipe__user-generated">
|
||||
<svg>
|
||||
<use href="${icons}#icon-user"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<button class="btn--round">
|
||||
<svg class="">
|
||||
<use href="${icons}#icon-bookmark-fill"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="recipe__ingredients">
|
||||
<h2 class="heading--2">Recipe ingredients</h2>
|
||||
<ul class="recipe__ingredient-list">
|
||||
${recipe.ingredients
|
||||
.map(ing => {
|
||||
return `
|
||||
<li class="recipe__ingredient">
|
||||
<svg class="recipe__icon">
|
||||
<use href="${icons}#icon-check"></use>
|
||||
</svg>
|
||||
<div class="recipe__quantity">${ing.quantity}</div>
|
||||
<div class="recipe__description">
|
||||
<span class="recipe__unit">${ing.unit}</span>
|
||||
${ing.description}
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="recipe__directions">
|
||||
<h2 class="heading--2">How to cook it</h2>
|
||||
<p class="recipe__directions-text">
|
||||
This recipe was carefully designed and tested by
|
||||
<span class="recipe__publisher">${
|
||||
recipe.publisher
|
||||
}</span>. Please check out
|
||||
directions at their website.
|
||||
</p>
|
||||
<a
|
||||
class="btn--small recipe__btn"
|
||||
href="h${recipe.sourceUrl}"
|
||||
target="_blank"
|
||||
>
|
||||
<span>Directions</span>
|
||||
<svg class="search__icon">
|
||||
<use href="src/img/icons.svg#icon-arrow-right"></use>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
recipeContainer.innerHTML = '';
|
||||
recipeContainer.insertAdjacentHTML('afterbegin', markup);
|
||||
recipeView.render(model.state.recipe);
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
|
||||
['hashchange', 'load'].forEach(ev => window.addEventListener(ev, showRecipe));
|
||||
['hashchange', 'load'].forEach(ev =>
|
||||
window.addEventListener(ev, controlRecipes)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
export const state = {
|
||||
recipe: {},
|
||||
};
|
||||
|
||||
export const loadRecipe = async function (id) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://forkify-api.herokuapp.com/api/v2/recipes/${id}`
|
||||
);
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) throw new Error(`${data.message} (${res.status})`);
|
||||
|
||||
const { recipe } = data.data;
|
||||
|
||||
state.recipe = {
|
||||
id: recipe.id,
|
||||
title: recipe.title,
|
||||
publisher: recipe.publisher,
|
||||
sourceUrl: recipe.source_url,
|
||||
image: recipe.image_url,
|
||||
servings: recipe.servings,
|
||||
cookingTime: recipe.cooking_time,
|
||||
ingredients: recipe.ingredients,
|
||||
};
|
||||
|
||||
console.log(state.recipe);
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
import icons from '../../img/icons.svg';
|
||||
import fractional from 'fractional';
|
||||
|
||||
class RecipeView {
|
||||
#parentElement = document.querySelector('.recipe');
|
||||
#data;
|
||||
|
||||
render(data) {
|
||||
this.#data = data;
|
||||
const markup = this.#generateMarkup();
|
||||
this.#clear();
|
||||
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
|
||||
}
|
||||
|
||||
#clear() {
|
||||
this.#parentElement.innerHTML = '';
|
||||
}
|
||||
|
||||
renderSpinner = function () {
|
||||
const markup = `
|
||||
<div class="spinner">
|
||||
<svg>
|
||||
<use href="${icons}#icon-loader"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
this.#parentElement.innerHTML = '';
|
||||
this.#parentElement.insertAdjacentHTML('afterbegin', markup);
|
||||
};
|
||||
|
||||
#generateMarkup() {
|
||||
return `
|
||||
<figure class="recipe__fig">
|
||||
<img src="${this.#data.image}" alt="${
|
||||
this.#data.title
|
||||
}" class="recipe__img" />
|
||||
<h1 class="recipe__title">
|
||||
<span>${this.#data.title}</span>
|
||||
</h1>
|
||||
</figure>
|
||||
|
||||
<div class="recipe__details">
|
||||
<div class="recipe__info">
|
||||
<svg class="recipe__info-icon">
|
||||
<use href="${icons}#icon-clock"></use>
|
||||
</svg>
|
||||
<span class="recipe__info-data recipe__info-data--minutes">${
|
||||
this.#data.cookingTime
|
||||
}</span>
|
||||
<span class="recipe__info-text">minutes</span>
|
||||
</div>
|
||||
<div class="recipe__info">
|
||||
<svg class="recipe__info-icon">
|
||||
<use href="${icons}#icon-users"></use>
|
||||
</svg>
|
||||
<span class="recipe__info-data recipe__info-data--people">${
|
||||
this.#data.servings
|
||||
}</span>
|
||||
<span class="recipe__info-text">servings</span>
|
||||
|
||||
<div class="recipe__info-buttons">
|
||||
<button class="btn--tiny btn--increase-servings">
|
||||
<svg>
|
||||
<use href="${icons}#icon-minus-circle"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="btn--tiny btn--increase-servings">
|
||||
<svg>
|
||||
<use href="${icons}#icon-plus-circle"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recipe__user-generated">
|
||||
<svg>
|
||||
<use href="${icons}#icon-user"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<button class="btn--round">
|
||||
<svg class="">
|
||||
<use href="${icons}#icon-bookmark-fill"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="recipe__ingredients">
|
||||
<h2 class="heading--2">Recipe ingredients</h2>
|
||||
<ul class="recipe__ingredient-list">
|
||||
${this.#data.ingredients.map(this.#generateMarkupIngredient).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="recipe__directions">
|
||||
<h2 class="heading--2">How to cook it</h2>
|
||||
<p class="recipe__directions-text">
|
||||
This recipe was carefully designed and tested by
|
||||
<span class="recipe__publisher">${
|
||||
this.#data.publisher
|
||||
}</span>. Please check out
|
||||
directions at their website.
|
||||
</p>
|
||||
<a
|
||||
class="btn--small recipe__btn"
|
||||
href="h${this.#data.sourceUrl}"
|
||||
target="_blank"
|
||||
>
|
||||
<span>Directions</span>
|
||||
<svg class="search__icon">
|
||||
<use href="src/img/icons.svg#icon-arrow-right"></use>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#generateMarkupIngredient(ing) {
|
||||
return `
|
||||
<li class="recipe__ingredient">
|
||||
<svg class="recipe__icon">
|
||||
<use href="${icons}#icon-check"></use>
|
||||
</svg>
|
||||
<div class="recipe__quantity">${
|
||||
ing.quantity ? new Fraction(ing.quantity).toString() : ''
|
||||
}</div>
|
||||
<div class="recipe__description">
|
||||
<span class="recipe__unit">${ing.unit}</span>
|
||||
${ing.description}
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default new RecipeView();
|
||||
Reference in New Issue
Block a user