diff --git a/js/add-effects-scale/index.js b/js/add-effects-scale/index.js index e46f716..53252ac 100644 --- a/js/add-effects-scale/index.js +++ b/js/add-effects-scale/index.js @@ -1,7 +1,7 @@ const scaleControlSmaller = document.querySelector('.scale__control--smaller'); const scaleControlBigger = document.querySelector('.scale__control--bigger'); const scaleControlValue = document.querySelector('.scale__control--value'); -const uploaPreviewImage = document.querySelector('.img-upload__preview img'); +const uploadPreviewImage = document.querySelector('.img-upload__preview img'); const STEP_SCALE = 25; const MIN_SCALE = 25; @@ -12,7 +12,7 @@ const onIncreaseScale = () => { const scaleCount = scaleValue + STEP_SCALE; const scaleLimited = scaleValue >= MAX_SCALE ? MAX_SCALE : scaleCount; scaleControlValue.value = `${scaleLimited}%`; - uploaPreviewImage.style.transform = `scale(${scaleLimited / 100})`; + uploadPreviewImage.style.transform = `scale(${scaleLimited / 100})`; }; const onDecreaseScale = () => { @@ -20,7 +20,7 @@ const onDecreaseScale = () => { const scaleCount = scaleValue - STEP_SCALE; const scaleLimited = scaleValue <= MIN_SCALE ? MIN_SCALE : scaleCount; scaleControlValue.value = `${scaleLimited}%`; - uploaPreviewImage.style.transform = `scale(${scaleLimited / 100})`; + uploadPreviewImage.style.transform = `scale(${scaleLimited / 100})`; }; const scaleListener = () => { diff --git a/js/api/get-data-variables.js b/js/api/get-data-variables.js index 134b189..873ce75 100644 --- a/js/api/get-data-variables.js +++ b/js/api/get-data-variables.js @@ -12,7 +12,6 @@ export const submitButtonText = { IDLE: 'Опубликовать', SENDING: 'Загрузка...' }; - export const Method = { GET: 'GET', POST: 'POST', diff --git a/js/api/secondary-functions.js b/js/api/secondary-functions.js index f633649..5498e9f 100644 --- a/js/api/secondary-functions.js +++ b/js/api/secondary-functions.js @@ -3,12 +3,11 @@ import {dataErrorTemplate, sendErrorTemplate, sendFormErrorTemplate, SHOW_ERROR_ const submitButton = document.querySelector('.img-upload__submit'); - -const onSendSuccessClose = () => { +function onSendSuccessClose() { const successMessage = document.querySelector('.success'); successMessage.remove(); document.removeEventListener('click', onClickOutModalSuccess); -}; +} const onSendErrorMessageClose = () => { const errorMessage = document.querySelector('.error'); @@ -56,11 +55,16 @@ const onSendErrorMessage = () => { document.removeEventListener('keydown', onCloseChangePhotoEsc); }; -const onShowErrorGetData = () => { +const onShowErrorGetData = (textError) => { const errorFragment = document.createDocumentFragment(); const errorItem = dataErrorTemplate.cloneNode(true); + + errorFragment.append(errorItem); document.body.append(errorFragment); + if(textError){ + errorItem.querySelector('.data-error__title').textContent = textError; + } const dataError = document.querySelector('.data-error'); setTimeout(() => { diff --git a/js/create-thumbnails/index.js b/js/create-thumbnails/index.js index 7314282..c0ce591 100644 --- a/js/create-thumbnails/index.js +++ b/js/create-thumbnails/index.js @@ -3,9 +3,15 @@ import {onOpenBigPicture } from '../show-large-picture'; const pictureTemplate = document.querySelector('#picture').content.querySelector('.picture'); const pictureList = document.querySelector('.pictures'); +const removePhoto = () => { + const parentElement = document.querySelector('.pictures'); + const allPicture = parentElement.querySelectorAll('.picture'); + allPicture.forEach((element) => element.remove()); +}; + const addPhotoThumbnailsUsers = (photoUsers) => { const listFragmentPhoto = document.createDocumentFragment(); - // createPhotoUsers = photoUsers; + removePhoto(); photoUsers.forEach((element) => { const pictureItem = pictureTemplate.cloneNode(true); const pictureImage = pictureItem.querySelector('.picture__img'); diff --git a/js/data/index.js b/js/data/index.js deleted file mode 100644 index 3e95b93..0000000 --- a/js/data/index.js +++ /dev/null @@ -1,65 +0,0 @@ -const NAMES = [ - 'Александр', - 'Екатерина', - 'Иван', - 'Мария', - 'Сергей', - 'Ольга', - 'Дмитрий', - 'Наталья', - 'Андрей', - 'Анна', - 'Павел', - 'Юлия', - 'Владимир', - 'Елена', - 'Николай', - 'Татьяна', - 'Максим', - 'Светлана', - 'Артем', - 'Алиса', - 'Константин', - 'Оксана', - 'Григорий', - 'Виктория', - 'Роман' -]; - -const MESSAGES = [ - 'Всё отлично!', - 'В целом всё неплохо. Но не всё.', - 'Когда вы делаете фотографию, хорошо бы убирать палец из кадра. В конце концов это просто непрофессионально.', - 'Моя бабушка случайно чихнула с фотоаппаратом в руках и у неё получилась фотография лучше.', - 'Я поскользнулся на банановой кожуре и уронил фотоаппарат на кота и у меня получилась фотография лучше.', - 'Лица у людей на фотке перекошены, как будто их избивают. Как можно было поймать такой неудачный момент?!', -]; - -const DESCRIPTION = [ - 'Яркий закат над горизонтом, окрашивающий небо в оттенки огня.', - 'Полевые маки, расцветающие под лучами утреннего солнца.', - 'Заснеженная горная вершина, величественно возвышающаяся над окружающей местностью.', - 'Дети, играющие на пляже и смеющиеся от радости.', - 'Старый заброшенный замок, погруженный в туманную атмосферу.', - 'Цветущие ярко-красные розы, распускающие свои лепестки.', - 'Пейзаж с извилистой рекой, отражающей облака на своей гладкой поверхности.', - 'Мистический лес, в котором кажется, что скрываются волшебные существа.', - 'Абстрактное изображение геометрических фигур, создающих ощущение движения.', - 'Парящий в небе воздушный шар, добавляющий яркости и красок в серую облачную дымку.', - 'Портрет старика с мудрыми глазами, отражающими его богатый жизненный опыт.', - 'Горящий костер на берегу озера, освещающий окружающую местность теплым светом.', - 'Архитектурное чудо в виде современного небоскреба, высокого и изящного.', - 'Забавные животные, играющие весело на зеленой поляне.', - 'Причудливые облака на фоне горизонта, создающие формы и узоры.', - 'Романтическая пара, гуляющая по закатной набережной и держащаяся за руки.', - 'Капли дождя, скатывающиеся по стеклу автомобиля и отражающие мир за окном.', - 'Снежинки, падающие на зимний пейзаж и придавая ему сказочный вид.', - 'Величественный водопад, образующий радужный мосток из брызг и пара.', - 'Старинная улица с колоритными домами и уютными кафе.', - 'Подсолнухи, поворачивающие свои головы за солнцем и создающие живописный пейзаж.', - 'Морской пляж с мягким песком и бирюзовой водой, приглашающий к отдыху и релаксации.', - 'Дикая природа с высокими соснами и горными вершинами на заднем плане.', - 'Разноцветные фейерверки, освещающие ночное небо яркими огнями.', -]; - -export {NAMES, MESSAGES, DESCRIPTION}; diff --git a/js/filters/filter-variables.js b/js/filters/filter-variables.js new file mode 100644 index 0000000..2b0e8fb --- /dev/null +++ b/js/filters/filter-variables.js @@ -0,0 +1,12 @@ +const filtersControlPanel = document.querySelector('.img-filters'); +const sortFunc = { + random: () => 0.5 - Math.random(), + discussed: (a, b) => b.comments.length - a.comments.length +}; +const buttonValue = { + random: 'filter-random', + discussed: 'filter-discussed', +}; +const RANDOM_PHOTO_LIMIT = 10; + +export {filtersControlPanel, sortFunc, buttonValue, RANDOM_PHOTO_LIMIT}; diff --git a/js/filters/index.js b/js/filters/index.js new file mode 100644 index 0000000..5d0fb78 --- /dev/null +++ b/js/filters/index.js @@ -0,0 +1,52 @@ +import {filtersControlPanel, sortFunc, buttonValue, RANDOM_PHOTO_LIMIT} from './filter-variables'; +import {debounce} from '../util/index'; +import {addPhotoThumbnailsUsers} from '../create-thumbnails'; + +let puctures = []; +let currentButton = ''; + +const showFilterPanel = () => { + filtersControlPanel.classList.remove('img-filters--inactive'); +}; + +const debounceRender = debounce(addPhotoThumbnailsUsers); + +const setActiveFilter = (evt) => { + const target = evt.target; + const activeButton = document.querySelector('.img-filters__button--active'); + + if(!target.matches('button')){ + return; + } + if(target === activeButton){ + return; + } + activeButton.classList.toggle('img-filters__button--active'); + target.classList.toggle('img-filters__button--active'); + currentButton = target.id; + filterChange(); +}; + +function filterChange () { + let addPhotoThumbnails = []; + switch (currentButton) { + case buttonValue.random: + addPhotoThumbnails = puctures.toSorted(sortFunc.random).slice(0, RANDOM_PHOTO_LIMIT); + break; + case buttonValue.discussed: + addPhotoThumbnails = puctures.toSorted(sortFunc.discussed); + break; + default: + addPhotoThumbnails = puctures; + } + + debounceRender(addPhotoThumbnails); +} + +const listenerButtonsFilter = (photos) => { + filtersControlPanel.addEventListener('click', setActiveFilter); + showFilterPanel(); + puctures = photos; +}; + +export {showFilterPanel, listenerButtonsFilter}; diff --git a/js/generate-data/createComments.js b/js/generate-data/createComments.js deleted file mode 100644 index 8bf885f..0000000 --- a/js/generate-data/createComments.js +++ /dev/null @@ -1,18 +0,0 @@ -import {getRandomInteger, createId, getRandomArrayElement} from '../util'; -import {NAMES, MESSAGES} from '../data'; - -const AVATAR_MIN = 1; -const AVATAR_MAX = 6; -const COMMENTS_MIN = 0; -const COMMENTS_MAX = 30; - -const commentsId = createId(); - -const createComments = () =>({ - id: commentsId(), - avatar: `img/avatar-${ getRandomInteger(AVATAR_MIN, AVATAR_MAX) }.svg`, - message: getRandomArrayElement(MESSAGES), - name: getRandomArrayElement(NAMES), -}); - -export const generateComment = () => Array.from({ length: getRandomInteger(COMMENTS_MIN, COMMENTS_MAX) }, createComments); diff --git a/js/generate-data/index.js b/js/generate-data/index.js deleted file mode 100644 index f9c5946..0000000 --- a/js/generate-data/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import {getRandomInteger, createId, getRandomArrayElement} from '../util'; -import {DESCRIPTION} from '../data'; -import {generateComment} from './createComments'; - - -const LIKES_MIN = 15; -const LIKES_MAX = 200; -const MAX_NUMBER_PHOTO_ID = 25; - -const generatePhotoId = createId(); -const generatePhotoName = createId(); - - -const createPhoto = () => ({ - id : generatePhotoId(), - url: `photos/${ generatePhotoName() }.jpg`, - description: getRandomArrayElement(DESCRIPTION), - likes: getRandomInteger(LIKES_MIN, LIKES_MAX), - comments: generateComment() -}); - -export const photoItems = () => Array.from({ length: MAX_NUMBER_PHOTO_ID }, createPhoto); diff --git a/js/main.js b/js/main.js index 9c51f41..d8b3387 100644 --- a/js/main.js +++ b/js/main.js @@ -5,11 +5,13 @@ import {effectCheckedListener} from './slider-effects'; import {getData} from './api'; import {addPhotoThumbnailsUsers, elementPhotoListener} from './create-thumbnails'; import {onShowErrorGetData } from './api/secondary-functions'; +import {listenerButtonsFilter} from './filters'; getData() .then((photos) => { addPhotoThumbnailsUsers(photos); elementPhotoListener(photos); + listenerButtonsFilter(photos); }).catch(() => { onShowErrorGetData(); }); diff --git a/js/show-large-picture/elementVariables.js b/js/show-large-picture/elementVariables.js index 60ee7b0..8270a5a 100644 --- a/js/show-large-picture/elementVariables.js +++ b/js/show-large-picture/elementVariables.js @@ -8,4 +8,3 @@ export const socialCaption = bigPictureBlock.querySelector('.social__caption'); export const socialCommentsList = bigPictureBlock.querySelector('.social__comments'); export const buttonShowMore = bigPictureBlock.querySelector('.social__comments-loader'); export const socialCommentChild = socialCommentsList.children; - diff --git a/js/show-large-picture/index.js b/js/show-large-picture/index.js index 4a64971..c178a5a 100644 --- a/js/show-large-picture/index.js +++ b/js/show-large-picture/index.js @@ -35,4 +35,3 @@ function onCloseBigPictureEsc(evt){ } export {onOpenBigPicture}; - diff --git a/js/slider-effects/effect-data.js b/js/slider-effects/effect-data.js index 055bb8f..d8c7670 100644 --- a/js/slider-effects/effect-data.js +++ b/js/slider-effects/effect-data.js @@ -49,4 +49,3 @@ export const EFFECTS = [ unit: '' } ]; - diff --git a/js/slider-effects/index.js b/js/slider-effects/index.js index 6dbb6a8..72bc6e8 100644 --- a/js/slider-effects/index.js +++ b/js/slider-effects/index.js @@ -1,4 +1,4 @@ -import {effectLevelSliderParrent, effectLevelSlider, effectLevelInput, effectChecked, uploaPreviewImage} from './slider-variables'; +import {effectLevelSliderParrent, effectLevelSlider, effectLevelInput, effectChecked, uploadPreviewImage} from './slider-variables'; import {EFFECTS, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_START } from './effect-data'; const sliderVisableToggle = (isShown = true) => { @@ -13,14 +13,25 @@ noUiSlider.create(effectLevelSlider, { max: DEFAULT_MAX, }, start: DEFAULT_START, - connect: 'lower' + connect: 'lower', + format: { + to: function (value) { + if (Number.isInteger(value)) { + return value.toFixed(0); + } + return value.toFixed(1); + }, + from: function (value) { + return parseFloat(value); + }, + }, }); const sliderUpdateOptions = (value) => { const effect = searhEffect(value, EFFECTS); if(!effect){ sliderVisableToggle(); - uploaPreviewImage.style.removeProperty('filter'); + uploadPreviewImage.style.removeProperty('filter'); return; } const {min, max, start, step, unit} = effect; @@ -36,7 +47,7 @@ const sliderUpdateOptions = (value) => { effectLevelSlider.noUiSlider.on('update', () => { const effectLevelInputValue = effectLevelSlider.noUiSlider.get(); effectLevelInput.value = effectLevelInputValue; - uploaPreviewImage.style.filter = `${effect.style}(${effectLevelInputValue}${unit})`; + uploadPreviewImage.style.filter = `${effect.style}(${effectLevelInputValue}${unit})`; }); }; diff --git a/js/slider-effects/slider-variables.js b/js/slider-effects/slider-variables.js index 75619b2..43556fb 100644 --- a/js/slider-effects/slider-variables.js +++ b/js/slider-effects/slider-variables.js @@ -2,4 +2,4 @@ export const effectLevelSliderParrent = document.querySelector('.img-upload__eff export const effectLevelSlider = effectLevelSliderParrent.querySelector('.effect-level__slider'); export const effectLevelInput = document.querySelector('.effect-level__value'); export const effectChecked = document.querySelector('.effects'); -export const uploaPreviewImage = document.querySelector('.img-upload__preview img'); +export const uploadPreviewImage = document.querySelector('.img-upload__preview img'); diff --git a/js/upload-photo/index.js b/js/upload-photo/index.js index 3ca0c14..d2a3803 100644 --- a/js/upload-photo/index.js +++ b/js/upload-photo/index.js @@ -1,16 +1,15 @@ import {toggleClass} from '../util'; -import {imgUploadInput, imgUpoadOverlay, imgUploadancel} from './uploadPhotoVariables'; +import {pristine} from '../validation-form'; +import {imgUploadInput, imgUpoadOverlay, imgUploadancel, effectsPreview, effectLevelSliderParrent, uploadPreviewImage, FILE_TYPES} from './uploadPhotoVariables'; import {inputTextHashtag, commentForm, imgUploadForm} from '../validation-form'; - -const effectsPreview = document.querySelectorAll('.effects__preview'); -const effectLevelSliderParrent = document.querySelector('.img-upload__effect-level'); -const uploaPreviewImage = document.querySelector('.img-upload__preview img'); +import {onShowErrorGetData } from '../api/secondary-functions'; const onCloseChangePhoto = () => { + pristine.reset(); toggleClass(imgUpoadOverlay, false); imgUploadForm.reset(); - uploaPreviewImage.style.removeProperty('filter'); - uploaPreviewImage.style.removeProperty('transform'); + uploadPreviewImage.style.removeProperty('filter'); + uploadPreviewImage.style.removeProperty('transform'); document.removeEventListener('keydown', onCloseChangePhotoEsc); imgUploadancel.removeEventListener('click', onCloseChangePhoto); }; @@ -23,40 +22,34 @@ function onCloseChangePhotoEsc(evt){ } } -const selectImage = (evt) => { - const files = evt.target.files; - const countFiles = files.length; - const selectedFile = files[0]; - const reader = new FileReader(); +const onOpenChangePhoto = () => { + toggleClass(imgUpoadOverlay); + effectLevelSliderParrent.classList.add('hidden'); + document.addEventListener('keydown', onCloseChangePhotoEsc); + imgUploadancel.addEventListener('click', onCloseChangePhoto); +}; - if (!countFiles) { - return; - } +const onSelectImage = () => { + const selectedFile = imgUploadInput.files[0]; + const fileName = selectedFile.name.toLowerCase(); + const fileUrl = URL.createObjectURL(selectedFile); + const matches = FILE_TYPES.some((it) => fileName.endsWith(it)); - if (!/^image/.test(selectedFile.type)) { + if (!matches) { + onShowErrorGetData('Неверный тип файла'); return; } - reader.readAsDataURL(selectedFile); - - reader.addEventListener('load', (e) => { - uploaPreviewImage.src = e.target.result; - effectsPreview.forEach((element) => { - element.style.backgroundImage = `url(${e.target.result})`; - }); + uploadPreviewImage.src = fileUrl; + effectsPreview.forEach((element) => { + element.style.backgroundImage = `url(${fileUrl})`; }); -}; -const onOpenChangePhoto = (evt) => { - toggleClass(imgUpoadOverlay); - selectImage(evt); - effectLevelSliderParrent.classList.add('hidden'); - document.addEventListener('keydown', onCloseChangePhotoEsc); - imgUploadancel.addEventListener('click', onCloseChangePhoto); + onOpenChangePhoto(); }; const onOpenChangePhotoListener = () => { - imgUploadInput.addEventListener('change', onOpenChangePhoto); + imgUploadInput.addEventListener('change', onSelectImage); }; export {onOpenChangePhotoListener, onCloseChangePhoto, onCloseChangePhotoEsc}; diff --git a/js/upload-photo/uploadPhotoVariables.js b/js/upload-photo/uploadPhotoVariables.js index e6628a7..1da4c9e 100644 --- a/js/upload-photo/uploadPhotoVariables.js +++ b/js/upload-photo/uploadPhotoVariables.js @@ -1,3 +1,7 @@ export const imgUploadInput = document.querySelector('.img-upload__input'); export const imgUpoadOverlay = document.querySelector('.img-upload__overlay'); export const imgUploadancel = imgUpoadOverlay.querySelector('.img-upload__cancel'); +export const effectsPreview = document.querySelectorAll('.effects__preview'); +export const effectLevelSliderParrent = document.querySelector('.img-upload__effect-level'); +export const uploadPreviewImage = document.querySelector('.img-upload__preview img'); +export const FILE_TYPES = ['.jpg', '.jpeg', '.png']; diff --git a/js/util/index.js b/js/util/index.js index 150517b..7f6a3cd 100644 --- a/js/util/index.js +++ b/js/util/index.js @@ -1,7 +1,9 @@ +const TIMEOUT_DELAY_DEBOUNCE = 500; + 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; // не понимаю эту запись , что тут происходит и для чего. + const result = Math.random() * (upper - lower + 1) + lower; return Math.floor(result); }; @@ -20,4 +22,12 @@ const toggleClass = (className, isOpened = true) =>{ document.body.classList.toggle('modal-open'); }; -export {getRandomInteger, createId, getRandomArrayElement, toggleClass}; +const debounce = (callback, timeoutDelay = TIMEOUT_DELAY_DEBOUNCE) => { + let timeoutId; + return (...rest) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => callback.apply(this, rest), timeoutDelay); + }; +}; + +export {getRandomInteger, createId, getRandomArrayElement, toggleClass, debounce}; diff --git a/js/validation-form/index.js b/js/validation-form/index.js index b13c197..58f7a9d 100644 --- a/js/validation-form/index.js +++ b/js/validation-form/index.js @@ -2,7 +2,6 @@ import {sendData} from '../api'; import {onSendSuccessMessage, onSendErrorMessage, blockSubmitButton, unblockSubmitButton} from '../api/secondary-functions'; import {onCloseChangePhoto} from '../upload-photo'; - export const imgUploadForm = document.querySelector('.img-upload__form'); export const inputTextHashtag = imgUploadForm.querySelector('.text__hashtags'); export const commentForm = imgUploadForm.querySelector('.text__description'); @@ -67,6 +66,4 @@ const validateListener = () => { }); }; -export {validateListener}; - - +export {validateListener, pristine};