mirror of
https://github.com/Nezumi-2711/practice-js.git
synced 2026-09-22 13:38:49 +00:00
Refactoring code to MVC architecture
This commit is contained in:
Generated
+17
-1
@@ -10,11 +10,13 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.32.0",
|
"core-js": "^3.32.0",
|
||||||
|
"fractional": "^1.0.0",
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-sass": "^2.9.3",
|
"@parcel/transformer-sass": "^2.9.3",
|
||||||
"parcel": "^2.9.3"
|
"parcel": "^2.9.3",
|
||||||
|
"process": "^0.11.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -2592,6 +2594,11 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
@@ -3337,6 +3344,15 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/react-error-overlay": {
|
||||||
"version": "6.0.9",
|
"version": "6.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||||
|
|||||||
+3
-1
@@ -10,10 +10,12 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-sass": "^2.9.3",
|
"@parcel/transformer-sass": "^2.9.3",
|
||||||
"parcel": "^2.9.3"
|
"parcel": "^2.9.3",
|
||||||
|
"process": "^0.11.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.32.0",
|
"core-js": "^3.32.0",
|
||||||
|
"fractional": "^1.0.0",
|
||||||
"regenerator-runtime": "^0.14.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) {
|
const timeout = function (s) {
|
||||||
return new Promise(function (_, reject) {
|
return new Promise(function (_, reject) {
|
||||||
@@ -14,150 +16,23 @@ const timeout = function (s) {
|
|||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
const renderSpinner = function (parentEl) {
|
const controlRecipes = async function () {
|
||||||
const markup = `
|
|
||||||
<div class="spinner">
|
|
||||||
<svg>
|
|
||||||
<use href="${icons}#icon-loader"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
parentEl.innerHTML = '';
|
|
||||||
parentEl.insertAdjacentHTML('afterbegin', markup);
|
|
||||||
};
|
|
||||||
|
|
||||||
const showRecipe = async function () {
|
|
||||||
try {
|
try {
|
||||||
const id = window.location.hash.slice(1);
|
const id = window.location.hash.slice(1);
|
||||||
|
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
recipeView.renderSpinner();
|
||||||
|
|
||||||
// 1)Loading recipe
|
// 1)Loading recipe
|
||||||
renderSpinner(recipeContainer);
|
await model.loadRecipe(id);
|
||||||
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);
|
|
||||||
|
|
||||||
// 2) Rendering recipe
|
// 2) Rendering recipe
|
||||||
const markup = `
|
recipeView.render(model.state.recipe);
|
||||||
<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);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(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