Implementing Bookmarks Function

This commit is contained in:
2023-08-16 17:26:41 +07:00
parent 57f41be9fd
commit 7c528a5934
9 changed files with 186 additions and 178 deletions
+2 -150
View File
@@ -63,20 +63,6 @@
No bookmarks yet. Find a nice recipe and bookmark it :)
</p>
</div>
<!-- <li class="preview">
<a class="preview__link" href="#23456">
<figure class="preview__fig">
<img src="src/img/test-1.jpg" alt="Test" />
</figure>
<div class="preview__data">
<h4 class="preview__name">
Pasta with Tomato Cream ...
</h4>
<p class="preview__publisher">The Pioneer Woman</p>
</div>
</a>
</li> -->
</ul>
</div>
</li>
@@ -85,41 +71,9 @@
</header>
<div class="search-results">
<ul class="results">
<!--
<li class="preview">
<a class="preview__link preview__link--active" href="#23456">
<figure class="preview__fig">
<img src="src/img/test-1.jpg" alt="Test" />
</figure>
<div class="preview__data">
<h4 class="preview__title">Pasta with Tomato Cream ...</h4>
<p class="preview__publisher">The Pioneer Woman</p>
<div class="preview__user-generated">
<svg>
<use href="src/img/icons.svg#icon-user"></use>
</svg>
</div>
</div>
</a>
</li>
-->
</ul>
<ul class="results"></ul>
<div class="pagination">
<!-- <button class="btn--inline pagination__btn--prev">
<svg class="search__icon">
<use href="src/img/icons.svg#icon-arrow-left"></use>
</svg>
<span>Page 1</span>
</button>
<button class="btn--inline pagination__btn--next">
<span>Page 3</span>
<svg class="search__icon">
<use href="src/img/icons.svg#icon-arrow-right"></use>
</svg>
</button> -->
</div>
<div class="pagination"></div>
<p class="copyright">
&copy; Copyright by
@@ -142,108 +96,6 @@
</div>
<p>Start by searching for a recipe or an ingredient. Have fun!</p>
</div>
<!-- <div class="spinner">
<svg>
<use href="src/img/icons.svg#icon-loader"></use>
</svg>
</div> -->
<!--
<figure class="recipe__fig">
<img src="src/img/test-1.jpg" alt="Tomato" class="recipe__img" />
<h1 class="recipe__title">
<span>Pasta with tomato cream sauce</span>
</h1>
</figure>
<div class="recipe__details">
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="src/img/icons.svg#icon-clock"></use>
</svg>
<span class="recipe__info-data recipe__info-data--minutes">45</span>
<span class="recipe__info-text">minutes</span>
</div>
<div class="recipe__info">
<svg class="recipe__info-icon">
<use href="src/img/icons.svg#icon-users"></use>
</svg>
<span class="recipe__info-data recipe__info-data--people">4</span>
<span class="recipe__info-text">servings</span>
<div class="recipe__info-buttons">
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="src/img/icons.svg#icon-minus-circle"></use>
</svg>
</button>
<button class="btn--tiny btn--increase-servings">
<svg>
<use href="src/img/icons.svg#icon-plus-circle"></use>
</svg>
</button>
</div>
</div>
<div class="recipe__user-generated">
<svg>
<use href="src/img/icons.svg#icon-user"></use>
</svg>
</div>
<button class="btn--round">
<svg class="">
<use href="src/img/icons.svg#icon-bookmark-fill"></use>
</svg>
</button>
</div>
<div class="recipe__ingredients">
<h2 class="heading--2">Recipe ingredients</h2>
<ul class="recipe__ingredient-list">
<li class="recipe__ingredient">
<svg class="recipe__icon">
<use href="src/img/icons.svg#icon-check"></use>
</svg>
<div class="recipe__quantity">1000</div>
<div class="recipe__description">
<span class="recipe__unit">g</span>
pasta
</div>
</li>
<li class="recipe__ingredient">
<svg class="recipe__icon">
<use href="src/img/icons.svg#icon-check"></use>
</svg>
<div class="recipe__quantity">0.5</div>
<div class="recipe__description">
<span class="recipe__unit">cup</span>
ricotta cheese
</div>
</li>
</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">The Pioneer Woman</span>. Please check out
directions at their website.
</p>
<a
class="btn--small recipe__btn"
href="http://thepioneerwoman.com/cooking/pasta-with-tomato-cream-sauce/"
target="_blank"
>
<span>Directions</span>
<svg class="search__icon">
<use href="src/img/icons.svg#icon-arrow-right"></use>
</svg>
</a>
</div>
-->
</div>
</div>
+32 -2
View File
@@ -2,7 +2,9 @@ import * as model from './model.js';
import recipeView from './views/recipeView.js';
import searchView from './views/searchView.js';
import resultsView from './views/resultsView.js';
import bookmarksView from './views/bookmarksView.js';
import paginationView from './views/paginationView.js';
import addRecipeView from './views/addRecipeView.js';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
@@ -21,10 +23,13 @@ const controlRecipes = async function () {
// 0) Update results view to mark selected search results
resultsView.update(model.getSearchResultsPage());
// 1)Loading recipe
// 1) Updating bookmarks view
bookmarksView.update(model.state.bookmarks);
// 2)Loading recipe
await model.loadRecipe(id);
// 2) Rendering recipe
// 3) Rendering recipe
recipeView.render(model.state.recipe);
} catch (error) {
recipeView.renderError(error);
@@ -69,11 +74,36 @@ const controlServings = function (newServings) {
recipeView.update(model.state.recipe);
};
const controlAddBookmark = function () {
// 1) Add/remove bookmark
if (!model.state.recipe.bookmarked) model.addBookmark(model.state.recipe);
else model.deleteBookmark(model.state.recipe.id);
// 2) Update recipe view
recipeView.update(model.state.recipe);
// 3) Render bookmarks
bookmarksView.render(model.state.bookmarks);
};
const controlBookmarks = function () {
bookmarksView.render(model.state.bookmarks);
};
const controlAddRecipe = function (newRecipe) {
console.log(newRecipe);
// Upload the new recipe data
};
const init = function () {
bookmarksView.addHandlerRender(controlBookmarks);
recipeView.addHandleRender(controlRecipes);
recipeView.addHandlerUpdateServings(controlServings);
recipeView.addHandlerAddBookmark(controlAddBookmark);
searchView.addHandlerSearch(controlSearchResults);
paginationView.addHandlerClick(controlPagination);
addRecipeView.addHandlerUpload(controlAddRecipe);
};
init();
+39
View File
@@ -10,6 +10,7 @@ export const state = {
page: 1,
resultsPerPage: RES_PER_PAGE,
},
bookmarks: [],
};
export const loadRecipe = async function (id) {
@@ -28,6 +29,10 @@ export const loadRecipe = async function (id) {
cookingTime: recipe.cooking_time,
ingredients: recipe.ingredients,
};
if (state.bookmarks.some(bookmark => bookmark.id === id))
state.recipe.bookmarked = true;
else state.recipe.bookmarked = false;
} catch (error) {
throw error;
}
@@ -47,6 +52,7 @@ export const loadSearchResults = async function (query) {
image: rec.image_url,
};
});
state.search.page = 1;
} catch (error) {
console.error(`${error} 💥💥💥`);
throw error;
@@ -69,3 +75,36 @@ export const updateServings = function (newServings) {
state.recipe.servings = newServings;
};
const persistBookmarks = function (recipe) {
localStorage.setItem('bookmarks', JSON.stringify(state.bookmarks));
};
export const addBookmark = function (recipe) {
// Add bookmark
state.bookmarks.push(recipe);
// Mark current recipe as bookmarks
if (recipe.id === state.recipe.id) state.recipe.bookmarked = true;
persistBookmarks();
};
export const deleteBookmark = function (id) {
// Delete bookmark
const index = state.bookmarks.findIndex(el => el.id === id);
state.bookmarks.splice(index, 1);
// Mark current recipe as NOT bookmarked
if (id === state.recipe.id) state.recipe.bookmarked = false;
persistBookmarks();
};
const init = function () {
const storage = localStorage.getItem('bookmarks');
if (storage) state.bookmarks = JSON.parse(storage);
};
init();
console.log(state.bookmarks);
+6 -3
View File
@@ -3,12 +3,15 @@ import icons from '../../img/icons.svg';
export default class View {
_data;
render(data) {
render(data, render = true) {
if (!data || (Array.isArray(data) && data.length === 0))
return this.renderError();
this._data = data;
const markup = this._generateMarkup();
if (!render) return markup;
this._clear();
this._parentElement.insertAdjacentHTML('afterbegin', markup);
}
@@ -23,12 +26,12 @@ export default class View {
newElements.forEach((newEl, i) => {
const curEl = curElements[i];
console.log(curEl, newEl.isEqualNode(curEl));
// Update change TEXT
if (
!newEl.isEqualNode(curEl) &&
newEl.firstChild.nodeValue.trim() !== ''
newEl.firstChild?.nodeValue.trim() !== '' &&
newEl.firstChild?.nodeValue.trim() !== undefined
) {
curEl.textContent = newEl.textContent;
}
+44
View File
@@ -0,0 +1,44 @@
import View from './View.js';
import icons from '../../img/icons.svg';
class AddRecipeView extends View {
_parentElement = document.querySelector('.upload');
_window = document.querySelector('.add-recipe-window');
_overlay = document.querySelector('.overlay');
_btnOpen = document.querySelector('.nav__btn--add-recipe');
_btnClose = document.querySelector('.btn--close-modal');
constructor() {
super();
this._addHandlerShowWindow();
this._addHandlerHideWindow();
}
toogleWindow() {
this._overlay.classList.toggle('hidden');
this._window.classList.toggle('hidden');
}
_addHandlerShowWindow() {
this._btnOpen.addEventListener('click', () => this.toogleWindow());
}
_addHandlerHideWindow() {
this._overlay.addEventListener('click', this.toogleWindow.bind(this));
this._btnClose.addEventListener('click', this.toogleWindow.bind(this));
}
addHandlerUpload(handler) {
this._parentElement.addEventListener('submit', function (e) {
e.preventDefault();
const dataArr = [...new FormData(this)];
const data = Object.fromEntries(dataArr);
handler(data);
});
}
_generateMarkup() {}
}
export default new AddRecipeView();
+21
View File
@@ -0,0 +1,21 @@
import View from './View.js';
import previewView from './previewView.js';
import icons from '../../img/icons.svg';
class BookmarksView extends View {
_parentElement = document.querySelector('.bookmarks__list');
_errorMessage = 'No bookmarks yet. Find a nice recipe and bookmark it!';
_message = '';
addHandlerRender(handler) {
window.addEventListener('load', handler);
}
_generateMarkup() {
return this._data
.map(bookmark => previewView.render(bookmark, false))
.join('');
}
}
export default new BookmarksView();
+28
View File
@@ -0,0 +1,28 @@
import View from './View.js';
import icons from '../../img/icons.svg';
class PreviewView extends View {
_parentElement = '';
_generateMarkup() {
const id = window.location.hash.slice(1);
return `
<li class="preview">
<a class="preview__link ${
this._data.id === id ? 'preview__link--active' : ''
}" href="#${this._data.id}">
<figure class="preview__fig">
<img src="${this._data.image}" alt="${this._data.title}" />
</figure>
<div class="preview__this._data">
<h4 class="preview__title">${this._data.title}</h4>
<p class="preview__publisher">${this._data.publisher}</p>
</div>
</a>
</li>
`;
}
}
export default new PreviewView();
+12 -2
View File
@@ -20,6 +20,14 @@ class RecipeView extends View {
});
}
addHandlerAddBookmark(handler) {
this._parentElement.addEventListener('click', function (e) {
const btn = e.target.closest('.btn--bookmark');
if (!btn) return;
handler();
});
}
_generateMarkup() {
return `
<figure class="recipe__fig">
@@ -70,9 +78,11 @@ class RecipeView extends View {
<div class="recipe__user-generated">
</div>
<button class="btn--round">
<button class="btn--round btn--bookmark">
<svg class="">
<use href="${icons}#icon-bookmark-fill"></use>
<use href="${icons}#icon-bookmark${
this._data.bookmarked ? '-fill' : ''
}"></use>
</svg>
</button>
</div>
+2 -21
View File
@@ -1,4 +1,5 @@
import View from './View.js';
import previewView from './previewView.js';
import icons from '../../img/icons.svg';
class ResultsView extends View {
@@ -7,27 +8,7 @@ class ResultsView extends View {
_message = '';
_generateMarkup() {
return this._data.map(this._generateMarkupPreview).join('');
}
_generateMarkupPreview(data) {
const id = window.location.hash.slice(1);
return `
<li class="preview">
<a class="preview__link ${
data.id === id ? 'preview__link--active' : ''
}" href="#${data.id}">
<figure class="preview__fig">
<img src="${data.image}" alt="${data.title}" />
</figure>
<div class="preview__data">
<h4 class="preview__title">${data.title}</h4>
<p class="preview__publisher">${data.publisher}</p>
</div>
</a>
</li>
`;
return this._data.map(result => previewView.render(result, false)).join('');
}
}