diff --git a/js/filters.js b/js/filters.js new file mode 100644 index 0000000..45b9937 --- /dev/null +++ b/js/filters.js @@ -0,0 +1,78 @@ +import { renderThumbnails } from './render-thumbnails.js'; +import { debounce } from './util.js'; + +const RANDOM_PHOTOS_COUNT = 10; +const RERENDER_DELAY = 500; +const ACTIVE_BUTTON_CLASS = 'img-filters__button--active'; + +const FilterId = { + DEFAULT: 'filter-default', + RANDOM: 'filter-random', + DISCUSSED: 'filter-discussed', +}; + +const filtersSection = document.querySelector('.img-filters'); +const filtersForm = document.querySelector('.img-filters__form'); + +let currentFilter = FilterId.DEFAULT; +let photos = []; + +const getRandomPhotos = (photosArray) => { + const shuffled = [...photosArray].sort(() => Math.random() - 0.5); + return shuffled.slice(0, RANDOM_PHOTOS_COUNT); +}; + +const getDiscussedPhotos = (photosArray) => + [...photosArray].sort((a, b) => b.comments.length - a.comments.length); + +const filterPhotos = () => { + switch (currentFilter) { + case FilterId.RANDOM: + return getRandomPhotos(photos); + case FilterId.DISCUSSED: + return getDiscussedPhotos(photos); + default: + return [...photos]; + } +}; + +const updateActiveButton = (clickedButton) => { + const activeButton = filtersForm.querySelector(`.${ACTIVE_BUTTON_CLASS}`); + if (activeButton) { + activeButton.classList.remove(ACTIVE_BUTTON_CLASS); + } + clickedButton.classList.add(ACTIVE_BUTTON_CLASS); +}; + +const debouncedRenderThumbnails = debounce( + () => renderThumbnails(filterPhotos()), + RERENDER_DELAY +); + +const onFilterButtonClick = (evt) => { + const clickedButton = evt.target.closest('.img-filters__button'); + + if (!clickedButton) { + return; + } + + if (clickedButton.id === currentFilter) { + return; + } + + currentFilter = clickedButton.id; + updateActiveButton(clickedButton); + debouncedRenderThumbnails(); +}; + +const showFilters = () => { + filtersSection.classList.remove('img-filters--inactive'); +}; + +const initFilters = (loadedPhotos) => { + photos = loadedPhotos; + showFilters(); + filtersForm.addEventListener('click', onFilterButtonClick); +}; + +export { initFilters }; diff --git a/js/main.js b/js/main.js index e5723c2..e0056d8 100644 --- a/js/main.js +++ b/js/main.js @@ -4,6 +4,7 @@ import { initImageEditor } from './image-editor.js'; import { getData } from './api.js'; import { showDataLoadError } from './messages.js'; import { setPhotos } from './full-size-picture.js'; +import { initFilters } from './filters.js'; // Инициализация формы загрузки и редактора изображений initUploadPhotoForm(); @@ -14,6 +15,7 @@ getData() .then((photos) => { setPhotos(photos); renderThumbnails(photos); + initFilters(photos); }) .catch((err) => { showDataLoadError(err.message); diff --git a/js/messages.js b/js/messages.js index 19fcc47..f5d6510 100644 --- a/js/messages.js +++ b/js/messages.js @@ -9,8 +9,9 @@ const errorTemplate = document.querySelector('#error').content.querySelector('.e // Показывает сообщение об ошибке загрузки данных -const showDataLoadError = () => { +const showDataLoadError = (message) => { const dataErrorElement = dataErrorTemplate.cloneNode(true); + dataErrorElement.querySelector('.data-error__title').textContent = message; document.body.appendChild(dataErrorElement); setTimeout(() => { @@ -18,11 +19,15 @@ const showDataLoadError = () => { }, ALERT_SHOW_TIME); }; - // Создает и показывает модальное сообщение (успех или ошибка) -const showMessage = (template, buttonClass) => { +const showMessage = (template, buttonClass, titleClass, message) => { const messageElement = template.cloneNode(true); + + if (message) { + messageElement.querySelector(titleClass).textContent = message; + } + document.body.appendChild(messageElement); const closeButton = messageElement.querySelector(buttonClass); @@ -52,11 +57,11 @@ const showMessage = (template, buttonClass) => { }; const showSuccessMessage = () => { - showMessage(successTemplate, '.success__button'); + showMessage(successTemplate, '.success__button', '.success__title'); }; -const showErrorMessage = () => { - showMessage(errorTemplate, '.error__button'); +const showErrorMessage = (message) => { + showMessage(errorTemplate, '.error__button', '.error__title', message); }; export { showDataLoadError, showSuccessMessage, showErrorMessage }; diff --git a/js/render-thumbnails.js b/js/render-thumbnails.js index 11bb92b..bb896da 100644 --- a/js/render-thumbnails.js +++ b/js/render-thumbnails.js @@ -1,4 +1,3 @@ -// import { photos } from './create-array-photos.js'; import { openBigPicture } from './full-size-picture.js'; const picturesNode = document.querySelector('.pictures'); @@ -26,7 +25,14 @@ const createPicture = ({id, url, description, comments, likes }) => { return pictureNode; }; +const clearThumbnails = () => { + const pictures = picturesNode.querySelectorAll('.picture'); + pictures.forEach((picture) => picture.remove()); +}; + const renderThumbnails = (pictures) => { + clearThumbnails(); + const picturesFragment = document.createDocumentFragment(); pictures.forEach((photo) => { diff --git a/js/util.js b/js/util.js index de3fb75..e246afd 100644 --- a/js/util.js +++ b/js/util.js @@ -1,35 +1,12 @@ +// Функция устранения дребезга (debounce) -//Функция получения случайного числа -const getRandomInteger = (a, b) => { - const lower = Math.ceil(Math.min(a, b)); - const upper = Math.floor(Math.max(a, b)); - const result = Math.random() * (upper - lower + 1) + lower; - return Math.floor(result); -}; - - -//Функция получения случайного уникального ID -const getRandomUniqueId = (min, max) => { - const previousValues = []; +const debounce = (callback, timeoutDelay = 500) => { + let timeoutId; - return function () { - let currentValue = getRandomInteger(min, max); - if (previousValues.length >= (max - min + 1)) { - return null; - } - while (previousValues.includes(currentValue)) { - currentValue = getRandomInteger(min, max); - } - previousValues.push(currentValue); - return currentValue; + return (...rest) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => callback.apply(this, rest), timeoutDelay); }; }; -//Функция получения случайного элемента массива -const getRandomArrayElement = (elements) => elements[getRandomInteger(0, elements.length - 1)]; - -export { - getRandomInteger, - getRandomUniqueId, - getRandomArrayElement, -}; +export { debounce };