diff --git a/src/mock/sort.js b/src/mock/sort.js index 41fb73d..9301f04 100644 --- a/src/mock/sort.js +++ b/src/mock/sort.js @@ -1,14 +1,13 @@ import {SortType} from './const.js'; import {getPointDuration} from '../utils.js'; -const sort = { +export const sort = { [SortType.DAY]: (points) => points.sort((point) => point.dateFrom), [SortType.EVENT]: (points) => points.sort((point) => point), [SortType.TIME]:(points) => points.sort((point) => getPointDuration(point.dateFrom, point.dateTo)), [SortType.PRICE]: (points) => points.sort((point) => point.basePrice), [SortType.OFFERS]: (points) => points.sort((point) => point), }; - export const generateSorter = (points) => ( Object.entries(sort).map(([sortType, sortPoints]) => ({ type: sortType, diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js new file mode 100644 index 0000000..3412ec6 --- /dev/null +++ b/src/presenter/point-presenter.js @@ -0,0 +1,124 @@ +import {render, replace, remove} from '../framework/render.js'; +import EditPointView from '../view/edit-point-view.js'; +import EventPointView from '../view/event-point-view.js'; + +const Mode = { + DEFAULT: 'DEFAULT', + EDITING: 'EDITING' +}; + +export default class PointPresenter { + #point = null; + #eventList = null; + #destinationsModel = null; + #offersModel = null; + #pointComponent = null; + #pointEditComponent = null; + #handleDataChange = null; + #handleModeChange = null; + #mode = Mode.DEFAULT; + + constructor({eventList, destinationsModel, offersModel, onDataChange, onModeChange}){ + this.#eventList = eventList; + this.#destinationsModel = destinationsModel; + this.#offersModel = offersModel; + this.#handleDataChange = onDataChange; + this.#handleModeChange = onModeChange; + } + + init(point){ + this.#point = point; + + const prevPointComponent = this.#pointComponent; + const prevPointEditComponent = this.#pointEditComponent; + + this.#pointComponent = new EventPointView({ + point: this.#point, + pointDestination: this.#destinationsModel.getById(this.#point.destination), + pointOffers: this.#offersModel.getByType(this.#point.type), + onEditClick: () => { + this.#replacePointToForm(); + document.addEventListener('keydown', this.#escKeyDownHandler); + }, + onFavoriteClick: () => { + this.#handleFavoriteClick(); + } + }); + + this.#pointEditComponent = new EditPointView({ + point: this.#point, + pointDestination: this.#destinationsModel.getById(this.#point.destination), + pointOffers: this.#offersModel.getByType(this.#point.type), + onSubmitClick: () => { + this.#handleFormSubmit(this.#point); + document.addEventListener('keydown', this.#escKeyDownHandler); + }, + onDeleteClick: () => { + // if (document.querySelectorAll('.trip-events__item').length - 1 === 0) { + // this.#renderNoPointComponent(); + // } + this.destroy(); + document.addEventListener('keydown', this.#escKeyDownHandler); + }, + onRollUpClick: () => { + this.#replaceFormToPoint(); + document.addEventListener('keydown', this.#escKeyDownHandler); + } + }); + + if (prevPointComponent === null || prevPointEditComponent === null) { + render(this.#pointComponent, this.#eventList.element); + return; + } + + if (this.#mode === Mode.DEFAULT){ + replace(this.#pointComponent, prevPointComponent); + } + + if (this.#mode === Mode.EDITING){ + replace(this.#pointEditComponent, prevPointEditComponent); + } + + remove(prevPointComponent); + remove(prevPointEditComponent); + } + + destroy(){ + remove(this.#pointComponent); + remove(this.#pointEditComponent); + } + + resetView() { + if (this.#mode !== Mode.DEFAULT) { + this.#replaceFormToPoint(); + } + } + + #escKeyDownHandler = (evt) => { + if (evt.key === 'Escape') { + evt.preventDefault(); + this.#replaceFormToPoint(); + document.removeEventListener('keydown', this.#escKeyDownHandler); + } + }; + + #handleFavoriteClick = () => { + this.#handleDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + }; + + #handleFormSubmit = (point) => { + this.#handleDataChange(point); + this.#replaceFormToPoint(); + }; + + #replacePointToForm() { + replace(this.#pointEditComponent, this.#pointComponent); + this.#handleModeChange(); + this.#mode = Mode.EDITING; + } + + #replaceFormToPoint() { + replace(this.#pointComponent, this.#pointEditComponent); + this.#mode = Mode.DEFAULT; + } +} diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index 88033bb..7d3f245 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -1,124 +1,130 @@ -import {render, replace, remove} from '../framework/render.js'; -import EditPointView from '../view/edit-point-view.js'; -import EventPointView from '../view/event-point-view.js'; +import {render, remove} from '../framework/render.js'; import EventListView from '../view/event-list-view.js'; import AddPointView from '../view/add-point-view.js'; -import EmptyListView from '../view/empty-list-view.js'; +import NoPointView from '../view/no-point-view.js'; import SortView from '../view/sort-view.js'; -import {generateSorter} from '../mock/sort.js'; +import PointPresenter from './point-presenter.js'; import TripInfoView from '../view/trip-info-view.js'; +import {updateItem} from '../utils.js'; export default class TripPresenter { #tripContainer = null; #destinationsModel = null; #offersModel = null; #pointsModel = null; + #tripEventsElement = null; + #tripInfoElement = null; + #points = []; + #newEventElement = null; + #pointPresenterArray = new Map(); constructor({tripContainer, destinationsModel, offersModel, pointsModel}) { this.#tripContainer = tripContainer; this.#destinationsModel = destinationsModel; this.#offersModel = offersModel; this.#pointsModel = pointsModel; + this.#tripEventsElement = this.#tripContainer.querySelector('.trip-events'); + this.#tripInfoElement = this.#tripContainer.querySelector('.trip-main'); + this.#newEventElement = document.querySelector('.trip-main__event-add-btn'); } + #sortComponent = null; #eventList = new EventListView(); - #emptyList = new EmptyListView(); + #noPointComponent = new NoPointView(); init(){ - const points = [...this.#pointsModel.get()]; - const tripInfoElement = this.#tripContainer.querySelector('.trip-main'); - const newEventElement = document.querySelector('.trip-main__event-add-btn'); - const tripEventsElement = this.#tripContainer.querySelector('.trip-events'); + this.#sortComponent = new SortView({ + points: this.#pointsModel.get() + }); + this.#points = [...this.#pointsModel.get()]; - if (this.#pointsModel.get().length === 0) { - render(new EmptyListView(), tripEventsElement); + if (this.#points.length === 0) { + this.#renderNoPointComponent(); return; } - newEventElement.addEventListener('click', () => this.#addPointHandler(newEventElement)); - const sorter = generateSorter(points); + this.#newEventElement.addEventListener('click', () => this.#addPointHandler(this.#newEventElement)); + + this.#renderTripInfo(); + this.#renderSort(); + this.#renderPoints(); + } + + //не снимаются обработчики + #addPointHandler(newEventElement) { + newEventElement.setAttribute('disabled', ''); + this.#renderAddPoint(newEventElement); + } + + #handlePointChange = (updatedPoint) => { + this.#points = updateItem(this.#points, updatedPoint); + this.#pointPresenterArray.get(updatedPoint.id).init(updatedPoint); + }; + #handleModeChange = () => { + this.#pointPresenterArray.forEach((presenter) => presenter.resetView()); + }; + + #renderTripInfo() { render(new TripInfoView({ - points: points, + points: this.#points, pointDestination: this.#destinationsModel.get(), pointOffers: this.#offersModel.get(), - }), tripInfoElement, 'afterbegin'); - render(new SortView({sorter}), tripEventsElement); - - render(this.#eventList, tripEventsElement); + }), this.#tripInfoElement, 'afterbegin'); + } - points.forEach((point) => { - this.#renderPoints(point); - }); + #renderSort() { + render(this.#sortComponent, this.#tripEventsElement); } + #renderNoPointComponent() { + render(this.#noPointComponent, this.#tripEventsElement); + remove(this.#sortComponent); + remove(this.#eventList); + } - #addPointHandler(newEventElement) { - newEventElement.setAttribute('disabled', ''); - this.#renderAddPoint(newEventElement); + #destroyNoPointComponent() { + remove(this.#noPointComponent); + render(this.#sortComponent, this.#tripEventsElement); + render(this.#eventList, this.#tripEventsElement); } - #renderPoints(point) { - const escKeyDownHandler = (evt) => { - if (evt.key === 'Escape') { - evt.preventDefault(); - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); - } - }; - - const eventPoint = new EventPointView({ - point: point, - pointDestination: this.#destinationsModel.getById(point.destination), - pointOffers: this.#offersModel.getByType(point.type), - onEditClick: () => { - replacePointToForm(); - document.addEventListener('keydown', escKeyDownHandler); - } + #renderPoint(point) { + const pointPresenter = new PointPresenter({ + eventList: this.#eventList, + destinationsModel: this.#destinationsModel, + offersModel: this.#offersModel, + onDataChange: this.#handlePointChange, + onModeChange: this.#handleModeChange }); - const eventEditPoint = new EditPointView({ - point: point, - pointDestination: this.#destinationsModel.getById(point.destination), - pointOffers: this.#offersModel.getByType(point.type), - onSubmitClick: () => { - replaceFormToPoint(); - document.addEventListener('keydown', escKeyDownHandler); - }, - onDeleteClick: () => { - if (document.querySelectorAll('.trip-events__item').length - 1 === 0) { - render(this.#emptyList, this.#tripContainer.querySelector('.trip-events')); - } - remove(eventEditPoint); - document.addEventListener('keydown', escKeyDownHandler); - }, - onRollUpClick: () => { - replaceFormToPoint(); - document.addEventListener('keydown', escKeyDownHandler); - } - }); + pointPresenter.init(point); + this.#pointPresenterArray.set(point.id, pointPresenter); + } - function replacePointToForm() { - replace(eventEditPoint, eventPoint); - } + #renderPoints() { + render(this.#eventList, this.#tripEventsElement); - function replaceFormToPoint() { - replace(eventPoint, eventEditPoint); - } + this.#points.forEach((point) => { + this.#renderPoint(point); + }); + } - render(eventPoint, this.#eventList.element); + #clearPointList() { + this.#pointPresenterArray.forEach((presenter) => presenter.destroy()); + this.#pointPresenterArray.clear(); } #renderAddPoint(newEventElement) { if (document.querySelectorAll('.trip-events__item').length - 1 !== 0) { - remove(this.#emptyList); + this.#destroyNoPointComponent(); } const eventAddPoint = new AddPointView({ pointOffers: this.#offersModel, onSaveClick: () => { }, onCancelClick: () => { - deleteForm(this.#emptyList); + deleteForm(this.#noPointComponent); } }); diff --git a/src/utils.js b/src/utils.js index 2ff6a31..1143967 100644 --- a/src/utils.js +++ b/src/utils.js @@ -38,7 +38,7 @@ export const getPointDuration = (dateFrom, dateTo) => { }; export const getRandomPictureElement = (city) => ({ - src: `https://loremflickr.com/248/152?random=${getRandomInt()}`, + src: `https://loremflickr.com/320/240/dog?random=${getRandomInt()}`, description: `${city} description` }); @@ -67,3 +67,5 @@ export const getDate = (add) => { }; export const getPicturesArray = (city) => Array.from({length: getRandomIntFromRange(0, 5)}, () => getRandomPictureElement(city)); + +export const updateItem = (items, update) => items.map((item) => item.id === update.id ? update : item); diff --git a/src/view/edit-point-view.js b/src/view/edit-point-view.js index ea2c2b0..f42335a 100644 --- a/src/view/edit-point-view.js +++ b/src/view/edit-point-view.js @@ -125,7 +125,7 @@ export default class EditPointView extends AbstractView{ this.#handleDeleteClick = onDeleteClick; this.#handleRollUpClick = onRollUpClick; - this.element.querySelector('.event__save-btn').addEventListener('submit', this.#submitClickHandler); + this.element.querySelector('.event__save-btn').addEventListener('click', this.#submitClickHandler); this.element.querySelector('.event__reset-btn').addEventListener('click', this.#deleteClickHandler); this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#rollUpClickHandler); } @@ -140,7 +140,7 @@ export default class EditPointView extends AbstractView{ #submitClickHandler = (evt) => { evt.preventDefault(); - this.#handleSubmitClick(); + this.#handleSubmitClick(this.#point); }; #deleteClickHandler = (evt) => { diff --git a/src/view/empty-list-view.js b/src/view/empty-list-view.js deleted file mode 100644 index 0724f00..0000000 --- a/src/view/empty-list-view.js +++ /dev/null @@ -1,9 +0,0 @@ -import AbstractView from '../framework/view/abstract-view.js'; - -const createTipEventListViewTemplate = () => '
Click New Event to create your first point
'; - -export default class EmptyListView extends AbstractView{ - get template() { - return createTipEventListViewTemplate(); - } -} diff --git a/src/view/event-point-view.js b/src/view/event-point-view.js index 4fd09ac..073dafe 100644 --- a/src/view/event-point-view.js +++ b/src/view/event-point-view.js @@ -53,8 +53,9 @@ export default class EventPointView extends AbstractView { #pointDestination = []; #pointOffers = []; #handleEditClick = null; + #handleFavoriteClick = null; - constructor({point = POINT_EMPTY, pointDestination, pointOffers, onEditClick}) { + constructor({point = POINT_EMPTY, pointDestination, pointOffers, onEditClick, onFavoriteClick}) { super(); this.#point = point; this.#pointDestination = pointDestination; @@ -62,6 +63,8 @@ export default class EventPointView extends AbstractView { this.#handleEditClick = onEditClick; this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler); + this.#handleFavoriteClick = onFavoriteClick; + this.element.querySelector('.event__favorite-btn').addEventListener('click', this.#favoriteClickHandler); } get template() { @@ -76,4 +79,9 @@ export default class EventPointView extends AbstractView { evt.preventDefault(); this.#handleEditClick(); }; + + #favoriteClickHandler = (evt) => { + evt.preventDefault(); + this.#handleFavoriteClick(); + }; } diff --git a/src/view/no-point-view.js b/src/view/no-point-view.js new file mode 100644 index 0000000..7eb99c8 --- /dev/null +++ b/src/view/no-point-view.js @@ -0,0 +1,9 @@ +import AbstractView from '../framework/view/abstract-view.js'; + +const createNoPointViewTemplate = () => 'Click New Event to create your first point
'; + +export default class NoPointView extends AbstractView{ + get template() { + return createNoPointViewTemplate(); + } +} diff --git a/src/view/sort-view.js b/src/view/sort-view.js index 295aa71..e5e8d76 100644 --- a/src/view/sort-view.js +++ b/src/view/sort-view.js @@ -1,4 +1,5 @@ import AbstractView from '../framework/view/abstract-view.js'; +import {generateSorter} from '../mock/sort.js'; const createSortItemTemplate = (sorter, isChecked) => { const {type} = sorter; @@ -11,8 +12,8 @@ const createSortItemTemplate = (sorter, isChecked) => { `); }; -const createSortTemplate = (sorterItems) => { - const sorterItemsTemplate = sorterItems.map((sorter, index) => createSortItemTemplate(sorter, index === 0)).join(''); +const createSortTemplate = (sort) => { + const sorterItemsTemplate = sort.map((sorter, index) => createSortItemTemplate(sorter, index === 0)).join(''); return (`