From 3ff70fd8a8f67344accd9d81c1e87c768f7e5d5c Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Mon, 13 Jan 2025 13:56:00 +0300 Subject: [PATCH 1/7] Realized taking data from server, catching errors, showing alerts on errors or success --- js/api.js | 43 ++++++++++++++++++++ js/big-picture-popup.js | 4 +- js/consts.js | 48 ++++------------------ js/get-photos.js | 45 --------------------- js/main.js | 11 +++-- js/render-pictures.js | 14 ++++--- js/upload-form.js | 90 ++++++++++++++++++++++++++++------------- js/utils.js | 46 +++++++++++++++------ 8 files changed, 165 insertions(+), 136 deletions(-) create mode 100644 js/api.js delete mode 100644 js/get-photos.js diff --git a/js/api.js b/js/api.js new file mode 100644 index 0000000..be19615 --- /dev/null +++ b/js/api.js @@ -0,0 +1,43 @@ +const BASE_URL = 'https://31.javascript.htmlacademy.pro/kekstagram'; + +const Route = { + GET_DATA: '/data', + SEND_DATA: '/' +}; + +const Method = { + GET: 'GET', + POST: 'POST' +}; +const load = (route, method = Method.GET, body = null) => fetch(`${BASE_URL}${route}`, { body, method }) + .then((response) => { + if (!response.ok) { + + throw new Error(); + } + + return response.json(); + }).catch(() => { + throw new Error('Ошибка при отправке/получении данных'); + }); +const getData = () => fetch(`${BASE_URL}${Route.GET_DATA}`).then((response) => response.json()); +const sendData = (body) => load(Route.SEND_DATA, Method.POST, body); +let getPictures = () => { + let pictures; + return async function () { + if (!pictures) { + try { + pictures = await getData(); + } catch { + throw new Error(); + } + } + return pictures; + + }; +}; + +getPictures = getPictures(); +export { sendData, getPictures }; + + diff --git a/js/big-picture-popup.js b/js/big-picture-popup.js index 9d2a09c..765e712 100644 --- a/js/big-picture-popup.js +++ b/js/big-picture-popup.js @@ -1,7 +1,5 @@ import { COMMENTS_STEP } from './consts.js'; -import { getPhotos } from './get-photos.js'; import { hide, show } from './utils.js'; -const photos = getPhotos(); const bigPhotoPopup = document.querySelector('.big-picture'); const popupCancelElement = document.querySelector('.big-picture__cancel'); const commentsLoadBtn = bigPhotoPopup.querySelector('.comments-loader'); @@ -72,7 +70,7 @@ function showPhotoPopup() { document.addEventListener('keydown', onEscapeKeydown); } -function openBigPhotoPopup(photoId) { +function openBigPhotoPopup(photos, photoId) { showPhotoPopup(); const currentPhoto = photos.find((photo) => photo.id === Number(photoId)); if (currentPhoto) { diff --git a/js/consts.js b/js/consts.js index 8a7d3af..509e6eb 100644 --- a/js/consts.js +++ b/js/consts.js @@ -1,41 +1,4 @@ -const NAMES = [ - 'Кристина', - 'Игорь', - 'Зина', - 'Петруша', - 'Макс', - 'Кирилл', - 'Алекс', - 'Коля', - 'Ксения', - 'Алина', -]; -const DESCRIPTIONS = [ - 'Солнечный берег с белым песком и пальмами.', - 'Ночной город с огнями и движением автомобилей.', - 'Улыбающиеся дети, играющие в парке.', - 'Закат над горами с яркими оранжевыми и красными оттенками.', - 'Стейк на гриле с овощами на стороне.', - 'Старый замок на вершине холма, окруженный туманом.', - 'Лесная тропинка среди осенних деревьев с разноцветной листвой.', - 'Пара, прогуливающаяся по мосту, держа друг друга за руки.', - 'Домашний офис с приятной атмосферой и растениями.', - 'Уютный кафе с чашкой кофе и книгой на столе.', - 'Дети, строящие снеговика на зимнем дворе.', - 'Спокойное озеро с отражением облаков на воде.', - 'Уличный рынок с яркими фруктами и овощами.', - 'Групповой снимок друзей на пляже.', - 'Кошка, спящая на окне в солнечный день.', -]; -const MESSAGES = [ - 'Всё отлично!', - 'В целом всё неплохо.Но не всё.', - 'Когда вы делаете фотографию, хорошо бы убирать палец из кадра.В конце концов это просто непрофессионально.', - 'Моя бабушка случайно чихнула с фотоаппаратом в руках и у неё получилась фотография лучше.', - 'Я поскользнулся на банановой кожуре и уронил фотоаппарат на кота и у меня получилась фотография лучше.', - 'Лица у людей на фотке перекошены, как будто их избивают.Как можно было поймать такой неудачный момент ? !', -]; -const FILTERS = { +const Filters = { chrome: { MIN_VALUE: 0, MAX_VALUE: 1, @@ -77,7 +40,12 @@ const FILTERS = { }, }; -const SCALE = { + +const AlertType = { + ERROR: 'error', + SUCCESS: 'success' +}; +const Scale = { MAX_SCALE: 100, MIN_SCALE: 25, SCALE_STEP: 25, @@ -86,6 +54,6 @@ const PHOTOS_COUNT = 25; const COMMENTS_STEP = 5; -export { NAMES, DESCRIPTIONS, MESSAGES, PHOTOS_COUNT, COMMENTS_STEP, FILTERS, SCALE }; +export { PHOTOS_COUNT, COMMENTS_STEP, Filters, Scale, AlertType }; diff --git a/js/get-photos.js b/js/get-photos.js deleted file mode 100644 index fbe4661..0000000 --- a/js/get-photos.js +++ /dev/null @@ -1,45 +0,0 @@ -import { DESCRIPTIONS, PHOTOS_COUNT, NAMES, MESSAGES } from './consts.js'; -import { getRandomInt, getUniqueId, getRandomElement } from './utils.js'; - - -const getCommentId = getUniqueId(1, 1000); - -function createComment() { - const commentId = getCommentId(); - const comment = { - id: commentId, - avatar: `img/avatar-${getRandomInt(1, 6)}.svg`, - message: getRandomElement(MESSAGES), - name: getRandomElement(NAMES), - }; - - return comment; -} - -function getComments() { - const comments = Array.from({ length: getRandomInt(0, 30) }, createComment); - return comments; -} - -const getPhotoId = getUniqueId(1, 25); -function createPhoto() { - const photoId = getPhotoId(); - const comments = getComments(); - - const photo = { - id: photoId, - url: `photos/${photoId}.jpg`, - description: getRandomElement(DESCRIPTIONS), - likes: getRandomInt(15, 200), - comments, - }; - - return photo; -} -const photos = Array.from({ length: PHOTOS_COUNT }, createPhoto); - -function getPhotos() { - return photos; -} - -export { getPhotos }; diff --git a/js/main.js b/js/main.js index e6f0ced..c5a3629 100644 --- a/js/main.js +++ b/js/main.js @@ -1,14 +1,17 @@ -import { renderPictures } from './render-pictures.js'; import { openBigPhotoPopup } from './big-picture-popup.js'; -import './upload-form.js'; +import { getPictures } from './api.js'; +import { closeUploadOverlay, setUploadFormSubmit } from './upload-form.js'; +import { renderPictures, showDataError } from './render-pictures.js'; -renderPictures(); +getPictures().then((photos) => renderPictures(photos)).catch(showDataError); document.addEventListener('click', (e) => { const currentPicture = e.target.closest('.picture'); if (currentPicture) { e.preventDefault(); - openBigPhotoPopup(currentPicture.dataset.photoId); + getPictures().then((photos) => openBigPhotoPopup(photos, currentPicture.dataset.photoId)); } }); + +setUploadFormSubmit(closeUploadOverlay); diff --git a/js/render-pictures.js b/js/render-pictures.js index ca73c25..c46a8ae 100644 --- a/js/render-pictures.js +++ b/js/render-pictures.js @@ -1,7 +1,6 @@ -import { getPhotos } from './get-photos.js'; -const photos = getPhotos(); +import { showElementFromTemplate } from './utils'; -function renderPictures() { +const renderPictures = (photos) => { const pictureTemplate = document.querySelector('#picture').content.querySelector('.picture'); const picturesList = document.querySelector('.pictures'); @@ -20,6 +19,11 @@ function renderPictures() { }); picturesList.append(picturesListFragment); -} +}; -export { renderPictures }; +const showDataError = () => { + const dataErrorElement = showElementFromTemplate('#data-error', 'Ошибка загрузки данных, обновите страницу'); + setTimeout(() => dataErrorElement.remove(), 5000); + +}; +export { renderPictures, showDataError }; diff --git a/js/upload-form.js b/js/upload-form.js index d4c9bb8..9a2c0e0 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -1,5 +1,6 @@ -import { show, hide } from './utils.js'; -import { SCALE, FILTERS } from './consts.js'; +import { show, hide, showElementFromTemplate, isEscapeKey, blockSubmitButton, activateSubmitButton } from './utils.js'; +import { Scale, Filters } from './consts.js'; +import { sendData } from './api.js'; const uploadForm = document.querySelector('#upload-select-image'); const uploadInput = uploadForm.querySelector('.img-upload__input'); const uploadOverlay = uploadForm.querySelector('.img-upload__overlay'); @@ -81,43 +82,76 @@ pristine.addValidator(descriptionInput, (value) => { return true; }, 'Максимальная длина комментария 140 символов'); function onUploadOverlayKeydown(e) { - if (e.key === 'Escape') { + const currentAlert = document.querySelector('.error') ?? document.querySelector('.success'); + if (isEscapeKey(e) && !currentAlert) { closeUploadOverlay(e); } } -function closeUploadOverlay(e) { +function closeUploadOverlay() { document.removeEventListener('keydown', onUploadOverlayKeydown); - e.preventDefault(); hide(uploadOverlay); document.body.classList.remove('modal-open'); uploadForm.reset(); + changeImgScale(); + + uploadForm.querySelector('input[name="effect"][value="none"]').dispatchEvent(new Event('change', { + bubbles: true + })); } +const showUploadOverlay = () => { + show(uploadOverlay); + document.body.classList.add('modal-open'); +}; + uploadInput.addEventListener('change', () => { showUploadOverlay(); document.addEventListener('keydown', onUploadOverlayKeydown); }); -function showUploadOverlay() { - show(uploadOverlay); - document.body.classList.add('modal-open'); - uploadForm.querySelector('input[name="effect"][value="none"]').dispatchEvent(new Event('change', { - bubbles: true - })); - changeImgScale(); +uploadFormCancelElem.addEventListener('click', closeUploadOverlay); +const closeAlertElement = () => { + const currentElement = document.querySelector('.error') ?? document.querySelector('.success'); + currentElement.remove(); + document.removeEventListener('keydown', onAlertKeydown); + document.removeEventListener('click', onDocumentClick); +}; +function onAlertKeydown(e) { + if (isEscapeKey(e)) { + closeAlertElement(); + } } -uploadFormCancelElem.addEventListener('click', closeUploadOverlay); -uploadForm.addEventListener('submit', (e) => { - if (!pristine.validate()) { - e.preventDefault(); - +function onDocumentClick(e) { + const currentElement = e.target.closest('.error__inner') ?? e.target.closest('.success__inner'); + if (!currentElement) { + closeAlertElement(); } +} -}); +function showAlert(type, message) { + const alertElement = showElementFromTemplate(`#${type}`, message); + alertElement.querySelector(`.${type}__button`).addEventListener('click', () => { + closeAlertElement(); + }); + + document.addEventListener('keydown', onAlertKeydown); + document.addEventListener('click', onDocumentClick); +} + +const setUploadFormSubmit = (onSuccess) => { + uploadForm.addEventListener('submit', (e) => { + e.preventDefault(); + if (pristine.validate()) { + blockSubmitButton(); + const formData = new FormData(e.target); + sendData(formData).then(() => showAlert('success', 'Данные успешно отправлены')).then(onSuccess).catch(() => showAlert('error', 'Ошибка при отправке данных')).finally(activateSubmitButton); + } + }); +}; //FILTERS FUNCTIONALITY const uploadFormPreviewImg = uploadForm.querySelector('.img-upload__preview'); @@ -140,10 +174,10 @@ function updateScale(isIncreasing) { let scaleValue = parseInt(scaleControl.value, 10); scaleValue = isIncreasing - ? scaleValue + SCALE.SCALE_STEP - : scaleValue - SCALE.SCALE_STEP; + ? scaleValue + Scale.SCALE_STEP + : scaleValue - Scale.SCALE_STEP; - scaleValue = Math.max(SCALE.MIN_SCALE, Math.min(SCALE.MAX_SCALE, scaleValue)); + scaleValue = Math.max(Scale.MIN_SCALE, Math.min(Scale.MAX_SCALE, scaleValue)); scaleControl.value = `${scaleValue}%`; } @@ -178,11 +212,11 @@ noUiSlider.create(effectsSlider, { function updateEffectsSlider(effect) { effectsSlider.noUiSlider.updateOptions({ range: { - max: FILTERS[effect].MAX_VALUE, - min: FILTERS[effect].MIN_VALUE, + max: Filters[effect].MAX_VALUE, + min: Filters[effect].MIN_VALUE, }, - step: FILTERS[effect].STEP, - start: FILTERS[effect].MAX_VALUE, + step: Filters[effect].STEP, + start: Filters[effect].MAX_VALUE, }); } @@ -196,7 +230,7 @@ effectsSlider.noUiSlider.on('update', () => { effectsValue.value = effectsSlider.noUiSlider.get(); const currentEffect = uploadForm.querySelector('input[name="effect"]:checked').value; if (!(currentEffect === 'none')) { - uploadFormPreviewImg.style.filter = `${FILTERS[currentEffect].EFFECT}(${effectsValue.value + FILTERS[currentEffect].UNIT_OF_MEASUREMENT}`; + uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; } }); @@ -208,9 +242,11 @@ uploadForm.querySelector('.img-upload__effects').addEventListener('change', (e) } else { show(sliderContainer); updateEffectsSlider(currentEffect); - uploadFormPreviewImg.style.filter = `${FILTERS[currentEffect].EFFECT}(${effectsValue.value + FILTERS[currentEffect].UNIT_OF_MEASUREMENT}`; + uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; } } }); hideEffectsContainer(); + +export { setUploadFormSubmit, closeUploadOverlay }; diff --git a/js/utils.js b/js/utils.js index 3cd7a8a..f2e71f4 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,17 +1,33 @@ -function getRandomInt(min, max) { +const SubmitButtonValue = { + IDLE: 'Опубликовать', + PENDING: 'Отправка...' +}; +const submitButton = document.querySelector('.img-upload__submit'); +const getRandomInt = (min, max) => { min = Math.ceil(min); max = Math.floor(max); const randomInt = Math.floor(Math.random() * (max - min + 1)) + min; return randomInt; -} +}; -function hide(elem) { +const hide = (elem) => { elem.classList.add('hidden'); -} -function show(elem) { +}; +const show = (elem) => { elem.classList.remove('hidden'); -} -function getUniqueId(min, max) { +}; + +const blockSubmitButton = () => { + submitButton.disabled = true; + submitButton.textContent = SubmitButtonValue.PENDING; +}; + +const activateSubmitButton = () => { + submitButton.disabled = false; + submitButton.textContent = SubmitButtonValue.IDLE; +}; + +const getUniqueId = (min, max) => { const receivedId = []; return function () { @@ -25,9 +41,15 @@ function getUniqueId(min, max) { receivedId.push(currentId); return currentId; }; -} -function getRandomElement(elements) { - return elements[getRandomInt(0, elements.length - 1)]; -} +}; + +const isEscapeKey = (e) => e.key === 'Escape'; +const getRandomElement = (elements) => elements[getRandomInt(0, elements.length - 1)]; +const showElementFromTemplate = (selector, message) => { + const dataElement = document.querySelector(selector).content.querySelector(`.${selector.slice(1)}`).cloneNode(true); + dataElement.querySelector('h2').textContent = message; + document.body.append(dataElement); + return dataElement; +}; -export { getRandomInt, getUniqueId, getRandomElement, hide, show }; +export { getRandomInt, getUniqueId, getRandomElement, hide, show, showElementFromTemplate, isEscapeKey, blockSubmitButton, activateSubmitButton }; From 02ca91d0cddd24b9d8aa2a56cb767d2aa087db97 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Mon, 13 Jan 2025 15:54:17 +0300 Subject: [PATCH 2/7] some fixes --- js/main.js | 2 +- js/upload-form.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/js/main.js b/js/main.js index c5a3629..aced48a 100644 --- a/js/main.js +++ b/js/main.js @@ -10,7 +10,7 @@ document.addEventListener('click', (e) => { if (currentPicture) { e.preventDefault(); - getPictures().then((photos) => openBigPhotoPopup(photos, currentPicture.dataset.photoId)); + getPictures().then((photos) => openBigPhotoPopup(photos, currentPicture.dataset.photoId), showDataError); } }); diff --git a/js/upload-form.js b/js/upload-form.js index 9a2c0e0..0b83dd9 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -148,7 +148,11 @@ const setUploadFormSubmit = (onSuccess) => { if (pristine.validate()) { blockSubmitButton(); const formData = new FormData(e.target); - sendData(formData).then(() => showAlert('success', 'Данные успешно отправлены')).then(onSuccess).catch(() => showAlert('error', 'Ошибка при отправке данных')).finally(activateSubmitButton); + sendData(formData) + .then(() => { + showAlert('success', 'Данные успешно отправлены'); + onSuccess(); + }).catch(() => showAlert('error', 'Ошибка при отправке данных')).finally(activateSubmitButton()); } }); }; From ac6600c42dc4ad6bd2fb7eefebd43829148e3079 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Tue, 14 Jan 2025 16:14:17 +0300 Subject: [PATCH 3/7] some refactoring --- js/upload-form.js | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/js/upload-form.js b/js/upload-form.js index 0b83dd9..89e62f0 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -1,5 +1,5 @@ import { show, hide, showElementFromTemplate, isEscapeKey, blockSubmitButton, activateSubmitButton } from './utils.js'; -import { Scale, Filters } from './consts.js'; +import { Scale, Filters, AlertType } from './consts.js'; import { sendData } from './api.js'; const uploadForm = document.querySelector('#upload-select-image'); const uploadInput = uploadForm.querySelector('.img-upload__input'); @@ -81,16 +81,18 @@ pristine.addValidator(descriptionInput, (value) => { } return true; }, 'Максимальная длина комментария 140 символов'); + +const getAlertElement = () => Object.values(AlertType).map((value) => document.querySelector(`.${value}`)).find((elem) => elem); function onUploadOverlayKeydown(e) { - const currentAlert = document.querySelector('.error') ?? document.querySelector('.success'); - if (isEscapeKey(e) && !currentAlert) { + if (isEscapeKey(e) && getAlertElement()) { + closeAlertElement(); + } else if (isEscapeKey(e) && !getAlertElement()) { closeUploadOverlay(e); } } function closeUploadOverlay() { document.removeEventListener('keydown', onUploadOverlayKeydown); - hide(uploadOverlay); document.body.classList.remove('modal-open'); uploadForm.reset(); @@ -113,20 +115,12 @@ uploadInput.addEventListener('change', () => { uploadFormCancelElem.addEventListener('click', closeUploadOverlay); -const closeAlertElement = () => { - const currentElement = document.querySelector('.error') ?? document.querySelector('.success'); - currentElement.remove(); - document.removeEventListener('keydown', onAlertKeydown); - document.removeEventListener('click', onDocumentClick); -}; -function onAlertKeydown(e) { - if (isEscapeKey(e)) { - closeAlertElement(); - } +function closeAlertElement() { + getAlertElement().remove(); } -function onDocumentClick(e) { - const currentElement = e.target.closest('.error__inner') ?? e.target.closest('.success__inner'); +function onAlertClick(e) { + const currentElement = e.target.closest('[class*="__inner"]'); if (!currentElement) { closeAlertElement(); } @@ -138,8 +132,7 @@ function showAlert(type, message) { closeAlertElement(); }); - document.addEventListener('keydown', onAlertKeydown); - document.addEventListener('click', onDocumentClick); + alertElement.addEventListener('click', onAlertClick); } const setUploadFormSubmit = (onSuccess) => { From 01bd579b5edaef91fab4aa2114506845cd9cc7a2 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Tue, 14 Jan 2025 16:53:02 +0300 Subject: [PATCH 4/7] deleted redundant listeners --- js/upload-form.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/js/upload-form.js b/js/upload-form.js index 89e62f0..3fe4d59 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -84,9 +84,7 @@ pristine.addValidator(descriptionInput, (value) => { const getAlertElement = () => Object.values(AlertType).map((value) => document.querySelector(`.${value}`)).find((elem) => elem); function onUploadOverlayKeydown(e) { - if (isEscapeKey(e) && getAlertElement()) { - closeAlertElement(); - } else if (isEscapeKey(e) && !getAlertElement()) { + if (isEscapeKey(e) && !getAlertElement()) { closeUploadOverlay(e); } } @@ -114,9 +112,15 @@ uploadInput.addEventListener('change', () => { }); uploadFormCancelElem.addEventListener('click', closeUploadOverlay); - +function onAlertKeydown(e) { + if (isEscapeKey(e)) { + //onAlertKeydown срабатывает раньше onUploadOverlayKeydown, поэтому при нажатии escape закрывается и форма и сообщение об ошибке + setTimeout(closeAlertElement, 0); + } +} function closeAlertElement() { - getAlertElement().remove(); + getAlertElement()?.remove(); + document.body.removeEventListener('keydown', onAlertKeydown); } function onAlertClick(e) { @@ -133,6 +137,7 @@ function showAlert(type, message) { }); alertElement.addEventListener('click', onAlertClick); + document.body.addEventListener('keydown', onAlertKeydown); } const setUploadFormSubmit = (onSuccess) => { From ed6a8cd787042c3aa0a61b3c5b995d571bd9ce70 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Tue, 14 Jan 2025 20:31:14 +0300 Subject: [PATCH 5/7] added filters module, and resolved some tasks after my code review --- js/api.js | 24 ++----- js/big-picture-popup.js | 14 ++--- js/filters.js | 100 +++++++++++++++++++++++++++++ js/main.js | 19 ++++-- js/upload-form.js | 136 +++++++--------------------------------- js/utils.js | 2 +- 6 files changed, 147 insertions(+), 148 deletions(-) create mode 100644 js/filters.js diff --git a/js/api.js b/js/api.js index be19615..fa45046 100644 --- a/js/api.js +++ b/js/api.js @@ -9,35 +9,21 @@ const Method = { GET: 'GET', POST: 'POST' }; + const load = (route, method = Method.GET, body = null) => fetch(`${BASE_URL}${route}`, { body, method }) .then((response) => { if (!response.ok) { - throw new Error(); } - return response.json(); }).catch(() => { throw new Error('Ошибка при отправке/получении данных'); }); -const getData = () => fetch(`${BASE_URL}${Route.GET_DATA}`).then((response) => response.json()); -const sendData = (body) => load(Route.SEND_DATA, Method.POST, body); -let getPictures = () => { - let pictures; - return async function () { - if (!pictures) { - try { - pictures = await getData(); - } catch { - throw new Error(); - } - } - return pictures; - }; -}; +const getData = () => load(`${BASE_URL}${Route.GET_DATA}`).then((response) => response.json()); + +const sendData = (body) => load(Route.SEND_DATA, Method.POST, body); -getPictures = getPictures(); -export { sendData, getPictures }; +export { sendData, getData }; diff --git a/js/big-picture-popup.js b/js/big-picture-popup.js index 765e712..eb16cfe 100644 --- a/js/big-picture-popup.js +++ b/js/big-picture-popup.js @@ -7,14 +7,14 @@ const commentsLoadBtn = bigPhotoPopup.querySelector('.comments-loader'); let currentComments = null; let currentShownCommentsCount = 0; -function onEscapeKeydown(e) { - if (e.key === 'Escape') { - hidePhotoPopup(e); +function onEscapeKeydown(evt) { + if (evt.key === 'Escape') { + hidePhotoPopup(evt); } } -function onLoadBtnClick(e) { - e.preventDefault(); +function onLoadBtnClick(evt) { + evt.preventDefault(); loadComments(); } @@ -56,8 +56,8 @@ function renderComments(comments) { currentComments = comments; loadComments(); } -function hidePhotoPopup(e) { - e.preventDefault(); +function hidePhotoPopup(evt) { + evt.preventDefault(); clearComments(); hide(bigPhotoPopup); document.removeEventListener('keydown', onEscapeKeydown); diff --git a/js/filters.js b/js/filters.js new file mode 100644 index 0000000..6618e3b --- /dev/null +++ b/js/filters.js @@ -0,0 +1,100 @@ +import { Filters, Scale } from './consts'; +import { hide, show } from './utils'; + +const uploadForm = document.querySelector('#upload-select-image'); +const uploadFormPreviewImg = uploadForm.querySelector('.img-upload__preview'); +const scaleControl = uploadForm.querySelector('.scale__control--value'); +const effectsSlider = uploadForm.querySelector('.effect-level__slider'); +const effectsValue = uploadForm.querySelector('.effect-level__value'); +const sliderContainer = uploadForm.querySelector('.img-upload__effect-level'); + +function onSmallerBtnClick(evt) { + evt.preventDefault(); + updateScale(false); + changeImgScale(); +} +function onBiggerBtnClick(evt) { + evt.preventDefault(); + updateScale(true); + changeImgScale(); +} +function updateScale(isIncreasing) { + let scaleValue = parseInt(scaleControl.value, 10); + + scaleValue = isIncreasing + ? scaleValue + Scale.SCALE_STEP + : scaleValue - Scale.SCALE_STEP; + + scaleValue = Math.max(Scale.MIN_SCALE, Math.min(Scale.MAX_SCALE, scaleValue)); + + scaleControl.value = `${scaleValue}%`; +} + +function changeImgScale() { + uploadFormPreviewImg.style.transform = `scale(${parseFloat(scaleControl.value) / 100})`; + +} +uploadForm.querySelector('.scale__control--smaller').addEventListener('click', onSmallerBtnClick); +uploadForm.querySelector('.scale__control--bigger').addEventListener('click', onBiggerBtnClick); + +noUiSlider.create(effectsSlider, { + range: { + max: 1, + min: 0, + }, + step: 0.1, + start: 1, + format: { + to: function (value) { + if (!Number.isInteger(value)) { + return value.toFixed(1); + } + return value; + }, + from: function (value) { + return value; + } + } +}); + +function updateEffectsSlider(effect) { + effectsSlider.noUiSlider.updateOptions({ + range: { + max: Filters[effect].MAX_VALUE, + min: Filters[effect].MIN_VALUE, + }, + step: Filters[effect].STEP, + start: Filters[effect].MAX_VALUE, + }); +} + +function hideEffectsContainer() { + hide(sliderContainer); + effectsValue.value = 0; + uploadFormPreviewImg.style.filter = 'none'; +} + +effectsSlider.noUiSlider.on('update', () => { + effectsValue.value = effectsSlider.noUiSlider.get(); + const currentEffect = uploadForm.querySelector('input[name="effect"]:checked').value; + if (!(currentEffect === 'none')) { + uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; + } +}); + +uploadForm.querySelector('.img-upload__effects').addEventListener('change', (evt) => { + const currentEffect = evt.target.closest('input[name="effect"]:checked').value; + if (currentEffect) { + if (currentEffect === 'none') { + hideEffectsContainer(); + } else { + show(sliderContainer); + updateEffectsSlider(currentEffect); + uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; + } + } +}); + +hideEffectsContainer(); + +export { changeImgScale }; diff --git a/js/main.js b/js/main.js index aced48a..98d1927 100644 --- a/js/main.js +++ b/js/main.js @@ -1,17 +1,24 @@ import { openBigPhotoPopup } from './big-picture-popup.js'; -import { getPictures } from './api.js'; +import { getData } from './api.js'; import { closeUploadOverlay, setUploadFormSubmit } from './upload-form.js'; import { renderPictures, showDataError } from './render-pictures.js'; -getPictures().then((photos) => renderPictures(photos)).catch(showDataError); +let pictures; -document.addEventListener('click', (e) => { - const currentPicture = e.target.closest('.picture'); +getData().then((photos) => { + pictures = photos; + renderPictures(pictures); +}) + .catch(showDataError); + +document.addEventListener('click', (evt) => { + const currentPicture = evt.target.closest('.picture'); if (currentPicture) { - e.preventDefault(); - getPictures().then((photos) => openBigPhotoPopup(photos, currentPicture.dataset.photoId), showDataError); + evt.preventDefault(); + openBigPhotoPopup(pictures, currentPicture.dataset.photoId); } + }); setUploadFormSubmit(closeUploadOverlay); diff --git a/js/upload-form.js b/js/upload-form.js index 3fe4d59..8e36629 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -1,6 +1,7 @@ import { show, hide, showElementFromTemplate, isEscapeKey, blockSubmitButton, activateSubmitButton } from './utils.js'; -import { Scale, Filters, AlertType } from './consts.js'; +import { AlertType } from './consts.js'; import { sendData } from './api.js'; +import { changeImgScale } from './filters.js'; const uploadForm = document.querySelector('#upload-select-image'); const uploadInput = uploadForm.querySelector('.img-upload__input'); const uploadOverlay = uploadForm.querySelector('.img-upload__overlay'); @@ -62,15 +63,15 @@ pristine.addValidator(hashtagsInput, (value) => { return true; }, HASHTAGS_ERRORS.invalidHashtag); -hashtagsInput.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - e.stopPropagation(); +hashtagsInput.addEventListener('keydown', (evt) => { + if (evt.key === 'Escape') { + evt.stopPropagation(); } }); -descriptionInput.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - e.stopPropagation(); +descriptionInput.addEventListener('keydown', (evt) => { + if (evt.key === 'Escape') { + evt.stopPropagation(); } }); pristine.addValidator(descriptionInput, (value) => { @@ -83,9 +84,9 @@ pristine.addValidator(descriptionInput, (value) => { }, 'Максимальная длина комментария 140 символов'); const getAlertElement = () => Object.values(AlertType).map((value) => document.querySelector(`.${value}`)).find((elem) => elem); -function onUploadOverlayKeydown(e) { - if (isEscapeKey(e) && !getAlertElement()) { - closeUploadOverlay(e); +function onUploadOverlayKeydown(evt) { + if (isEscapeKey(evt) && !getAlertElement()) { + closeUploadOverlay(evt); } } @@ -112,19 +113,18 @@ uploadInput.addEventListener('change', () => { }); uploadFormCancelElem.addEventListener('click', closeUploadOverlay); -function onAlertKeydown(e) { - if (isEscapeKey(e)) { - //onAlertKeydown срабатывает раньше onUploadOverlayKeydown, поэтому при нажатии escape закрывается и форма и сообщение об ошибке - setTimeout(closeAlertElement, 0); +function onAlertKeydown(evt) { + if (isEscapeKey(evt)) { + closeAlertElement(); } } function closeAlertElement() { getAlertElement()?.remove(); - document.body.removeEventListener('keydown', onAlertKeydown); + document.removeEventListener('keydown', onAlertKeydown); } -function onAlertClick(e) { - const currentElement = e.target.closest('[class*="__inner"]'); +function onAlertClick(evt) { + const currentElement = evt.target.closest('[class*="__inner"]'); if (!currentElement) { closeAlertElement(); } @@ -137,15 +137,15 @@ function showAlert(type, message) { }); alertElement.addEventListener('click', onAlertClick); - document.body.addEventListener('keydown', onAlertKeydown); + document.addEventListener('keydown', onAlertKeydown); } const setUploadFormSubmit = (onSuccess) => { - uploadForm.addEventListener('submit', (e) => { - e.preventDefault(); + uploadForm.addEventListener('submit', (evt) => { + evt.preventDefault(); if (pristine.validate()) { blockSubmitButton(); - const formData = new FormData(e.target); + const formData = new FormData(evt.target); sendData(formData) .then(() => { showAlert('success', 'Данные успешно отправлены'); @@ -154,101 +154,7 @@ const setUploadFormSubmit = (onSuccess) => { } }); }; - //FILTERS FUNCTIONALITY -const uploadFormPreviewImg = uploadForm.querySelector('.img-upload__preview'); -const scaleControl = uploadForm.querySelector('.scale__control--value'); -const effectsSlider = uploadForm.querySelector('.effect-level__slider'); -const effectsValue = uploadForm.querySelector('.effect-level__value'); -const sliderContainer = uploadForm.querySelector('.img-upload__effect-level'); - -function onSmallerBtnClick(e) { - e.preventDefault(); - updateScale(false); - changeImgScale(); -} -function onBiggerBtnClick(e) { - e.preventDefault(); - updateScale(true); - changeImgScale(); -} -function updateScale(isIncreasing) { - let scaleValue = parseInt(scaleControl.value, 10); - - scaleValue = isIncreasing - ? scaleValue + Scale.SCALE_STEP - : scaleValue - Scale.SCALE_STEP; - - scaleValue = Math.max(Scale.MIN_SCALE, Math.min(Scale.MAX_SCALE, scaleValue)); - - scaleControl.value = `${scaleValue}%`; -} - -function changeImgScale() { - uploadFormPreviewImg.style.transform = `scale(${parseFloat(scaleControl.value) / 100})`; - -} -uploadForm.querySelector('.scale__control--smaller').addEventListener('click', onSmallerBtnClick); -uploadForm.querySelector('.scale__control--bigger').addEventListener('click', onBiggerBtnClick); - -noUiSlider.create(effectsSlider, { - range: { - max: 1, - min: 0, - }, - step: 0.1, - start: 1, - format: { - to: function (value) { - if (!Number.isInteger(value)) { - return value.toFixed(1); - } - return value; - }, - from: function (value) { - return value; - } - } -}); - -function updateEffectsSlider(effect) { - effectsSlider.noUiSlider.updateOptions({ - range: { - max: Filters[effect].MAX_VALUE, - min: Filters[effect].MIN_VALUE, - }, - step: Filters[effect].STEP, - start: Filters[effect].MAX_VALUE, - }); -} - -function hideEffectsContainer() { - hide(sliderContainer); - effectsValue.value = 0; - uploadFormPreviewImg.style.filter = 'none'; -} - -effectsSlider.noUiSlider.on('update', () => { - effectsValue.value = effectsSlider.noUiSlider.get(); - const currentEffect = uploadForm.querySelector('input[name="effect"]:checked').value; - if (!(currentEffect === 'none')) { - uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; - } -}); - -uploadForm.querySelector('.img-upload__effects').addEventListener('change', (e) => { - const currentEffect = e.target.closest('input[name="effect"]:checked').value; - if (currentEffect) { - if (currentEffect === 'none') { - hideEffectsContainer(); - } else { - show(sliderContainer); - updateEffectsSlider(currentEffect); - uploadFormPreviewImg.style.filter = `${Filters[currentEffect].EFFECT}(${effectsValue.value + Filters[currentEffect].UNIT_OF_MEASUREMENT}`; - } - } -}); -hideEffectsContainer(); export { setUploadFormSubmit, closeUploadOverlay }; diff --git a/js/utils.js b/js/utils.js index f2e71f4..554b668 100644 --- a/js/utils.js +++ b/js/utils.js @@ -43,7 +43,7 @@ const getUniqueId = (min, max) => { }; }; -const isEscapeKey = (e) => e.key === 'Escape'; +const isEscapeKey = (evt) => evt.key === 'Escape'; const getRandomElement = (elements) => elements[getRandomInt(0, elements.length - 1)]; const showElementFromTemplate = (selector, message) => { const dataElement = document.querySelector(selector).content.querySelector(`.${selector.slice(1)}`).cloneNode(true); From c4fc249cb05b05218cbc34362e175e0e2cabea45 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Tue, 14 Jan 2025 20:51:09 +0300 Subject: [PATCH 6/7] fixed problems with api --- js/api.js | 3 ++- js/upload-form.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/js/api.js b/js/api.js index fa45046..ef2d032 100644 --- a/js/api.js +++ b/js/api.js @@ -12,6 +12,7 @@ const Method = { const load = (route, method = Method.GET, body = null) => fetch(`${BASE_URL}${route}`, { body, method }) .then((response) => { + if (!response.ok) { throw new Error(); } @@ -20,7 +21,7 @@ const load = (route, method = Method.GET, body = null) => fetch(`${BASE_URL}${ro throw new Error('Ошибка при отправке/получении данных'); }); -const getData = () => load(`${BASE_URL}${Route.GET_DATA}`).then((response) => response.json()); +const getData = () => load(Route.GET_DATA); const sendData = (body) => load(Route.SEND_DATA, Method.POST, body); diff --git a/js/upload-form.js b/js/upload-form.js index 8e36629..678ede8 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -145,6 +145,7 @@ const setUploadFormSubmit = (onSuccess) => { evt.preventDefault(); if (pristine.validate()) { blockSubmitButton(); + const formData = new FormData(evt.target); sendData(formData) .then(() => { From dd866e3478bad26c827e76fc49a8ceb3bebc15d4 Mon Sep 17 00:00:00 2001 From: Daniil888-m Date: Tue, 14 Jan 2025 22:57:53 +0300 Subject: [PATCH 7/7] final version --- js/api.js | 1 - js/upload-form.js | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/js/api.js b/js/api.js index ef2d032..9f5fec9 100644 --- a/js/api.js +++ b/js/api.js @@ -12,7 +12,6 @@ const Method = { const load = (route, method = Method.GET, body = null) => fetch(`${BASE_URL}${route}`, { body, method }) .then((response) => { - if (!response.ok) { throw new Error(); } diff --git a/js/upload-form.js b/js/upload-form.js index 678ede8..a7afabb 100644 --- a/js/upload-form.js +++ b/js/upload-form.js @@ -145,13 +145,13 @@ const setUploadFormSubmit = (onSuccess) => { evt.preventDefault(); if (pristine.validate()) { blockSubmitButton(); - const formData = new FormData(evt.target); - sendData(formData) - .then(() => { - showAlert('success', 'Данные успешно отправлены'); - onSuccess(); - }).catch(() => showAlert('error', 'Ошибка при отправке данных')).finally(activateSubmitButton()); + sendData(formData).then(() => { + showAlert('success', 'Данные успешно отправлены'); + onSuccess(); + }) + .catch(() => showAlert('error', 'Ошибка при отправке данных')) + .finally(activateSubmitButton); } }); };