forked from htmlacademy-javascript/101056-kekstagram-32
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
739 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const BASE_URL = 'https://32.javascript.htmlacademy.pro/kekstagram'; | ||
const API_ROUTE = { | ||
GET: `${BASE_URL}/data`, | ||
POST: `${BASE_URL}/`, | ||
}; | ||
|
||
|
||
const getData = (successCallback, errorCallback) => fetch(API_ROUTE.GET) | ||
.then((response) => { | ||
if (!response.ok) { | ||
throw new Error(`Ошибка сети: ${response.status} ${response.statusText}`); | ||
} | ||
return response.json(); | ||
}) | ||
.then(successCallback) | ||
.catch(errorCallback); | ||
|
||
const sendData = (formData) => | ||
fetch(API_ROUTE.POST, { | ||
method: 'POST', | ||
body: formData, | ||
}); | ||
|
||
export { getData, sendData }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { sortArrayDescending, shuffleArray, debounce } from './util.js'; | ||
import { renderThumbnailListWithRetry } from './render-thumbnails.js'; | ||
|
||
const RERENDER_DELAY = 500; | ||
const PICTURE_COUNT = 10; | ||
|
||
const imgFilters = document.querySelector('.img-filters'); | ||
|
||
const clearThumbnailList = () => { | ||
const thumbnails = document.querySelectorAll('.picture'); | ||
thumbnails.forEach((element) => { | ||
element.remove(); | ||
}); | ||
}; | ||
|
||
const changeThumbnailList = (evt, data) => { | ||
const filterActions = { | ||
'filter-default': () => data, | ||
'filter-random': () => shuffleArray(data.slice()).slice(0, PICTURE_COUNT), | ||
'filter-discussed': () => sortArrayDescending(data.slice(), (item) => item.comments.length), | ||
}; | ||
|
||
const filterButton = Object.keys(filterActions).find((filter) => evt.target.closest(`#${filter}`)); | ||
|
||
if (filterButton) { | ||
clearThumbnailList(); | ||
const filteredPhotoData = filterActions[filterButton](); | ||
renderThumbnailListWithRetry(filteredPhotoData); | ||
} | ||
}; | ||
|
||
const setFiltersClick = (data) => { | ||
const handleThumbnailChange = debounce((evt) => { | ||
changeThumbnailList(evt, data); | ||
}, RERENDER_DELAY); | ||
|
||
imgFilters.addEventListener('click', (evt) => { | ||
if (evt.target.classList.contains('img-filters__button')) { | ||
const buttons = imgFilters.querySelectorAll('.img-filters__button'); | ||
const button = evt.target.closest('.img-filters__button'); | ||
|
||
if ( | ||
(button && !button.classList.contains('img-filters__button--active')) | ||
) { | ||
buttons.forEach((btn) => btn.classList.remove('img-filters__button--active')); | ||
button.classList.add('img-filters__button--active'); | ||
|
||
handleThumbnailChange(evt); | ||
} | ||
} | ||
}); | ||
}; | ||
|
||
const showFilters = (data) => { | ||
imgFilters.classList.remove('img-filters--inactive'); | ||
setFiltersClick(data); | ||
}; | ||
|
||
export { showFilters }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const imgUploadScale = document.querySelector('.img-upload__scale'); | ||
const controlValue = imgUploadScale.querySelector('.scale__control--value'); | ||
const imgUploadPreview = document.querySelector('.img-upload__preview img'); | ||
|
||
const STEP = 25; | ||
|
||
const updateImageScale = (formStep) => { | ||
const value = parseInt(controlValue.value.slice(0, -1), 10); | ||
let scaleValue = value + formStep; | ||
|
||
if (scaleValue < 25) { | ||
scaleValue = 25; | ||
} else if (scaleValue > 100) { | ||
scaleValue = 100; | ||
} | ||
|
||
const transformValue = `scale(${scaleValue / 100})`; | ||
imgUploadPreview.style.transform = transformValue; | ||
controlValue.value = `${scaleValue}%`; | ||
}; | ||
|
||
const onFormClickScaleButtons = (evt) => { | ||
const clickedElementClassList = evt.target.classList; | ||
|
||
if (clickedElementClassList.contains('scale__control--bigger')) { | ||
updateImageScale(STEP); | ||
} else if (clickedElementClassList.contains('scale__control--smaller')) { | ||
updateImageScale(-STEP); | ||
} | ||
}; | ||
|
||
export { onFormClickScaleButtons, updateImageScale }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { validateHashtags, getErrorText, validateDescription } from './form-img-upload-validate.js'; | ||
import { isEscapeKey } from './util.js'; | ||
import { sendData } from './api.js'; | ||
|
||
const form = document.querySelector('.img-upload__form'); | ||
const formImgUploadOverlay = form.querySelector('.img-upload__overlay'); | ||
const templateSuccess = document.querySelector('#success'); | ||
const templateError = document.querySelector('#error'); | ||
const submitButton = form.querySelector('.img-upload__submit'); | ||
|
||
let notificationElement; | ||
let notificationCancel; | ||
let isResponseError; | ||
|
||
const pristine = new Pristine(form, { | ||
classTo: 'img-upload__field-wrapper', | ||
errorClass: 'img-upload__field-wrapper--error', | ||
errorTextParent: 'img-upload__field-wrapper', | ||
}); | ||
|
||
pristine.addValidator(form.querySelector('.text__hashtags'), validateHashtags, getErrorText); | ||
pristine.addValidator(form.querySelector('.text__description'), validateDescription, getErrorText); | ||
|
||
const resetPristin = () => { | ||
pristine.reset(); | ||
}; | ||
|
||
|
||
const closeNotification = () => { | ||
if (isResponseError) { | ||
formImgUploadOverlay.classList.remove('hidden'); | ||
} | ||
if (notificationCancel) { | ||
notificationCancel.removeEventListener('click', onNotificationClickCancel); | ||
notificationElement.removeEventListener('click', onNotificationClickElsewhere); | ||
} | ||
document.removeEventListener('keydown', onNotificationEsc); | ||
if (notificationElement) { | ||
notificationElement.remove(); | ||
notificationElement = null; | ||
} | ||
}; | ||
|
||
const showNotification = (isError) => { | ||
const clone = document.importNode(isError ? templateError.content : templateSuccess.content, true); | ||
|
||
if (notificationElement) { | ||
document.body.removeChild(notificationElement); | ||
} | ||
|
||
notificationElement = document.createElement('div'); | ||
notificationElement.appendChild(clone); | ||
document.body.appendChild(notificationElement); | ||
|
||
notificationCancel = document.querySelector(isError ? '.error__button' : '.success__button'); | ||
notificationCancel.addEventListener('click', onNotificationClickCancel); | ||
document.addEventListener('keydown', onNotificationEsc); | ||
notificationElement.addEventListener('click', onNotificationClickElsewhere); | ||
}; | ||
|
||
const blockSubmitButton = () => { | ||
submitButton.disabled = true; | ||
submitButton.textContent = 'Публикую'; | ||
}; | ||
|
||
const unblockSubmitButton = () => { | ||
submitButton.disabled = false; | ||
submitButton.textContent = 'Опубликовать'; | ||
}; | ||
|
||
const setUserFormSubmit = (onSuccess) => { | ||
form.addEventListener('submit', async (evt) => { | ||
evt.preventDefault(); | ||
|
||
const isValid = pristine.validate(); | ||
if (!isValid) { | ||
return; | ||
} | ||
|
||
const formData = new FormData(evt.target); | ||
blockSubmitButton(); | ||
|
||
try { | ||
const response = await sendData(formData); | ||
isResponseError = !response.ok; | ||
showNotification(isResponseError); | ||
onSuccess(isResponseError); | ||
} finally { | ||
unblockSubmitButton(); | ||
} | ||
}); | ||
}; | ||
|
||
function onNotificationClickCancel (evt) { | ||
evt.preventDefault(); | ||
closeNotification(); | ||
} | ||
|
||
function onNotificationClickElsewhere (evt) { | ||
if (!evt.target.closest('.error__inner') && isResponseError) { | ||
closeNotification(); | ||
} else if (!evt.target.closest('.success__inner') && !isResponseError) { | ||
closeNotification(); | ||
} | ||
} | ||
|
||
function onNotificationEsc (evt){ | ||
if (isEscapeKey(evt)) { | ||
evt.preventDefault(); | ||
closeNotification(); | ||
} | ||
} | ||
|
||
export { setUserFormSubmit, resetPristin }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
const filtersData = { | ||
none: { range: { min: 0, max: 1 }, step: 0.1 }, | ||
chrome: { range: { min: 0, max: 1 }, step: 0.1 }, | ||
sepia: { range: { min: 0, max: 1 }, step: 0.1 }, | ||
marvin: { range: { min: 0, max: 100 }, step: 1 }, | ||
phobos: { range: { min: 0, max: 3 }, step: 0.1 }, | ||
heat: { range: { min: 1, max: 3 }, step: 0.1 } | ||
}; | ||
|
||
let currentFilter; | ||
|
||
const formImgUploadWrapper = document.querySelector('.img-upload__wrapper'); | ||
const slider = formImgUploadWrapper.querySelector('.img-upload__effect-level'); | ||
const levelSlider = formImgUploadWrapper.querySelector('.effect-level__slider'); | ||
const levelValue = formImgUploadWrapper.querySelector('.effect-level__value'); | ||
const imgUploadPreview = formImgUploadWrapper.querySelector('.img-upload__preview img'); | ||
const noneRadioButton = formImgUploadWrapper.querySelector('#effect-none'); | ||
|
||
const changeFilter = (value) => { | ||
switch (currentFilter) { | ||
case 'none': imgUploadPreview.style.filter = ''; break; | ||
case 'chrome': imgUploadPreview.style.filter = `grayscale(${value})`; break; | ||
case 'sepia': imgUploadPreview.style.filter = `sepia(${value})`; break; | ||
case 'marvin': imgUploadPreview.style.filter = `invert(${value}%)`; break; | ||
case 'phobos': imgUploadPreview.style.filter = `blur(${value}px)`; break; | ||
case 'heat': imgUploadPreview.style.filter = `brightness(${value})`; break; | ||
} | ||
}; | ||
|
||
noUiSlider.create(levelSlider, { | ||
range: { | ||
min: filtersData.none.range.min, | ||
max: filtersData.none.range.max | ||
}, | ||
start: filtersData.none.range.max, | ||
step: filtersData.none.step, | ||
connect: 'lower', | ||
format: { | ||
to: (value) => (Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)), | ||
from: (value) => parseFloat(value) | ||
} | ||
}); | ||
|
||
levelSlider.noUiSlider.on('update', () => { | ||
levelValue.value = levelSlider.noUiSlider.get(); | ||
changeFilter(levelValue.value); | ||
}); | ||
|
||
levelSlider.setAttribute('disabled', true); | ||
|
||
const resetFilter = () => { | ||
currentFilter = 'none'; | ||
noneRadioButton.checked = true; | ||
levelSlider.noUiSlider.set(0); | ||
levelSlider.setAttribute('disabled', true); | ||
}; | ||
|
||
const onFormClickFilter = (evt) => { | ||
slider.classList.remove('hidden'); | ||
const clickedElementId = evt.target.id.split('-')[1]; | ||
if (clickedElementId !== 'none') { | ||
levelSlider.removeAttribute('disabled'); | ||
currentFilter = clickedElementId; | ||
levelSlider.noUiSlider.updateOptions({ | ||
range: { | ||
min: filtersData[clickedElementId].range.min, | ||
max: filtersData[clickedElementId].range.max | ||
}, | ||
start: filtersData[clickedElementId].range.max, | ||
step: filtersData[clickedElementId].step | ||
}); | ||
} else { | ||
slider.classList.add('hidden'); | ||
resetFilter(); | ||
} | ||
}; | ||
|
||
export { onFormClickFilter, resetFilter }; |
Oops, something went wrong.