From 232ebaaed91c5761d654990e0852543102bbab27 Mon Sep 17 00:00:00 2001 From: Sergei Pozdeev Date: Thu, 26 Sep 2024 13:30:58 -0400 Subject: [PATCH] 5.12. Big changes (part 1) --- src/constants.js | 5 ++ src/main.js | 4 +- src/presenter/main-presenter.js | 80 +++++++++++++++++++++ src/presenter/point-presenter.js | 115 ++++++++++++++++++++++++++++++ src/presenter/presenter.js | 89 ----------------------- src/util/utils.js | 2 +- src/view/event-add-button-view.js | 7 +- src/view/event-editor-view.js | 19 ++--- src/view/events-item-view.js | 21 ++++-- 9 files changed, 230 insertions(+), 112 deletions(-) create mode 100644 src/presenter/main-presenter.js create mode 100644 src/presenter/point-presenter.js delete mode 100644 src/presenter/presenter.js diff --git a/src/constants.js b/src/constants.js index 4014520..17c8163 100644 --- a/src/constants.js +++ b/src/constants.js @@ -91,3 +91,8 @@ export const FilterMessage = { [FilterType.PRESENT]: 'There are no present events now', [FilterType.PAST]: 'There are no past events now' }; + +export const Mode = { + DEFAULT: 'DEFAULT', + EDITING: 'EDITING', +}; diff --git a/src/main.js b/src/main.js index 3483094..1e56f31 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -import Presenter from './presenter/presenter.js'; +import MainPresenter from './presenter/main-presenter.js'; import PointModel from './model/point-model.js'; const tripMainElement = document.querySelector('.trip-main'); @@ -8,7 +8,7 @@ const filtersElement = document.querySelector('.trip-controls__filters'); const pointModel = new PointModel(); pointModel.init(); -const presenter = new Presenter({ +const presenter = new MainPresenter({ infoContainer: tripMainElement, contentContainer: tripEventsElement, filtersContainer: filtersElement, diff --git a/src/presenter/main-presenter.js b/src/presenter/main-presenter.js new file mode 100644 index 0000000..ca52105 --- /dev/null +++ b/src/presenter/main-presenter.js @@ -0,0 +1,80 @@ +import TripInfoView from '../view/trip-info-view.js'; +import EventsListView from '../view/events-list-view.js'; +import FiltersView from '../view/filters-view.js'; +import SortView from '../view/sort-view.js'; +import EventAddButtonView from '../view/event-add-button-view.js'; +import EventsMessageView from '../view/events-message-view.js'; +import PointPresenter from './point-presenter.js'; +import { render, RenderPosition } from '../framework/render.js'; +import { EVENTS_MESSAGE } from '../constants.js'; +import { generateFilter } from '../mocks/filters.js'; +import { updateItem } from '../util/utils.js'; + +export default class MainPresenter { + #tripInfoComponent = new TripInfoView(); + #addButtonComponent = new EventAddButtonView(); + #sortComponent = new SortView(); + #listComponent = new EventsListView(); + #pointModel = null; + #pointPresenters = new Map(); + + #points = []; + #offers = []; + #destinations = []; + + constructor({infoContainer, contentContainer, filtersContainer, pointModel}) { + this.infoContainer = infoContainer; + this.contentContainer = contentContainer; + this.filtersContainer = filtersContainer; + this.#pointModel = pointModel; + } + + init() { + this.#points = this.#pointModel.points; + this.#destinations = this.#pointModel.destinations; + this.#offers = this.#pointModel.offers; + const filters = generateFilter(this.#points); + + render(this.#tripInfoComponent, this.infoContainer, RenderPosition.AFTERBEGIN); + render(new FiltersView({filters}), this.filtersContainer); + render(this.#addButtonComponent, this.infoContainer); + + this.#renderWithoutContent(this.#points); + this.#renderContent(this.#points, this.#offers, this.#destinations); + } + + #renderWithoutContent = (points) => { + if (points.length === 0) { + render(new EventsMessageView(EVENTS_MESSAGE.EMPTY), this.contentContainer); + } + }; + + #renderContent = (points, offers, destinations) => { + render(this.#sortComponent, this.contentContainer); + render(this.#listComponent, this.contentContainer); + this.#renderPoints(points, offers, destinations); + }; + + #renderPoints = (points, offers, destinations) => { + points.forEach((point) => this.#renderPoint(point, offers, destinations)); + }; + + #renderPoint = (point, offers, destinations) => { + const listComponent = this.#listComponent.element; + const onDataChange = this.#handlePointChange; + const onModeChange = this.#handleModeChange; + const pointPresenter = new PointPresenter({ listComponent, onDataChange, onModeChange }); + + pointPresenter.init(point, offers, destinations); + this.#pointPresenters.set(point.id, pointPresenter); + }; + + #handlePointChange = (updatedPoint) => { + this.#points = updateItem(this.#points, updatedPoint); + this.#pointPresenters.get(updatedPoint.id).init(updatedPoint, this.#offers, this.#destinations); + }; + + #handleModeChange = () => { + this.#pointPresenters.forEach((presenter) => presenter.resetView()); + }; +} diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js new file mode 100644 index 0000000..c39dfed --- /dev/null +++ b/src/presenter/point-presenter.js @@ -0,0 +1,115 @@ +import { render, replace, remove } from '../framework/render.js'; +import { Mode } from '../constants.js'; + +import EventEditorView from '../view/event-editor-view.js'; +import EventsItemView from '../view/events-item-view.js'; + +export default class PointPresenter { + #listComponent = null; + #eventComponent = null; + #eventEditorComponent = null; + + #point = []; + #offers = []; + #destinations = []; + + #handleDataChange = null; + #handleModeChange = null; + + #mode = Mode.DEFAULT; + + constructor({listComponent, onDataChange, onModeChange}) { + this.#listComponent = listComponent; + this.#handleDataChange = onDataChange; + this.#handleModeChange = onModeChange; + } + + init(point, offers, destinations) { + this.#point = point; + this.#offers = offers; + this.#destinations = destinations; + + const prevEventComponent = this.#eventComponent; + const prevEventEditorComponent = this.#eventEditorComponent; + + this.#eventComponent = new EventsItemView({ + point: this.#point, + offers: this.#offers, + destinations: this.#destinations, + onEditClick: this.#handleOpenClick, + onFavoriteClick: this.#handleFavoriteClick + }); + + this.#eventEditorComponent = new EventEditorView({ + point: this.#point, + offers: this.#offers, + destinations: this.#destinations, + onEditClick: this.#handleCloseClick, + onFormSubmit: this.#handleFormSubmit + }); + + if (prevEventComponent === null || prevEventEditorComponent === null) { + render(this.#eventComponent, this.#listComponent); + return; + } + + if (this.#mode === Mode.DEFAULT) { + replace(this.#eventComponent, prevEventComponent); + } + + if (this.#mode === Mode.EDITING) { + replace(this.#eventEditorComponent, prevEventEditorComponent); + } + + remove(prevEventComponent); + remove(prevEventEditorComponent); + } + + destroy() { + remove(this.#eventComponent); + remove(this.#eventEditorComponent); + } + + resetView() { + if (this.#mode !== Mode.DEFAULT) { + this.#replaceFormToPoint(); + } + } + + #replacePointToForm = () => { + replace(this.#eventEditorComponent, this.#eventComponent); + document.addEventListener('keydown', this.#escKeyDownHandler); + this.#handleModeChange(); + this.#mode = Mode.EDITING; + }; + + #replaceFormToPoint = () => { + replace(this.#eventComponent, this.#eventEditorComponent); + document.removeEventListener('keydown', this.#escKeyDownHandler); + this.#mode = Mode.DEFAULT; + }; + + #escKeyDownHandler = (event) => { + if (event.key === 'Escape') { + event.preventDefault(); + this.#replaceFormToPoint(); + } + }; + + #handleOpenClick = () => { + this.#replacePointToForm(); + }; + + #handleCloseClick = () => { + this.#replaceFormToPoint(); + }; + + #handleFormSubmit = (point) => { + this.#handleDataChange(point); + this.#replaceFormToPoint(); + }; + + #handleFavoriteClick = () => { + this.#handleDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + }; +} diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js deleted file mode 100644 index c1e0ca2..0000000 --- a/src/presenter/presenter.js +++ /dev/null @@ -1,89 +0,0 @@ -import { render, RenderPosition, replace } from '../framework/render.js'; -import TripInfoView from '../view/trip-info-view.js'; -import EventsListView from '../view/events-list-view.js'; -import EventsItemView from '../view/events-item-view.js'; -import EventEditorView from '../view/event-editor-view.js'; -import FiltersView from '../view/filters-view.js'; -import SortView from '../view/sort-view.js'; -import EventAddButtonView from '../view/event-add-button-view.js'; -import { EVENTS_MESSAGE } from '../constants'; -import EventsMessageView from '../view/events-message-view.js'; -import {generateFilter} from '../mocks/filters'; - -export default class Presenter { - #tripInfoComponent = new TripInfoView(); - #addButtonComponent = new EventAddButtonView(); - #sortComponent = new SortView(); - #listComponent = new EventsListView(); - #pointModel = null; - - constructor({infoContainer, contentContainer, filtersContainer, pointModel}) { - this.infoContainer = infoContainer; - this.contentContainer = contentContainer; - this.filtersContainer = filtersContainer; - this.#pointModel = pointModel; - } - - init() { - const points = this.#pointModel.points; - const destinations = this.#pointModel.destinations; - const offers = this.#pointModel.offers; - const filters = generateFilter(points); - - render(this.#tripInfoComponent, this.infoContainer, RenderPosition.AFTERBEGIN); - render(new FiltersView({filters}), this.filtersContainer); - render(this.#addButtonComponent, this.infoContainer); - render(this.#sortComponent, this.contentContainer); - render(this.#listComponent, this.contentContainer); - - if (points.length === 0) { - render(new EventsMessageView(EVENTS_MESSAGE.EMPTY), this.infoContainer); - } - - points.forEach((point) => this.#renderPoint(point, destinations, offers)); - } - - #renderPoint(point, destinations, offers) { - const escKeyDownHandler = (event) => { - if (event.key === 'Escape') { - event.preventDefault(); - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); - } - }; - - const pointComponent = new EventsItemView({ - point, - destinations, - offers, - onEditClick: () => { - replacePointToForm(); - document.addEventListener('keydown', escKeyDownHandler); - } - }); - - const pointEditorComponent = new EventEditorView({ - point, - destinations, - offers, - onEditClick: () => { - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); - }, - onFormSubmit: () => { - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); - } - }); - - function replacePointToForm() { - replace(pointEditorComponent, pointComponent); - } - - function replaceFormToPoint() { - replace(pointComponent, pointEditorComponent); - } - - render(pointComponent, this.#listComponent.element); - } -} diff --git a/src/util/utils.js b/src/util/utils.js index 8096611..a415627 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -32,4 +32,4 @@ export const isDatePast = (end) => dayjs().isAfter(end); export const getCapitalized = (word) => `${word[0].toUpperCase()}${word.slice(1)}`; - +export const updateItem = (items, update) => items.map((item) => item.id === update.id ? update : item); diff --git a/src/view/event-add-button-view.js b/src/view/event-add-button-view.js index cd90e85..99fd264 100644 --- a/src/view/event-add-button-view.js +++ b/src/view/event-add-button-view.js @@ -1,10 +1,7 @@ import AbstractView from '../framework/view/abstract-view.js'; -function createEventAddButtonTemplate() { - return ` - - `; -} +const createEventAddButtonTemplate = () => ` + `; export default class EventAddButtonView extends AbstractView { get template() { diff --git a/src/view/event-editor-view.js b/src/view/event-editor-view.js index 50aa3a8..b146500 100644 --- a/src/view/event-editor-view.js +++ b/src/view/event-editor-view.js @@ -67,7 +67,7 @@ const createEventDetailsTemplate = (defaultOffers, selectedOffers, description, `; }; -const createEventEditorTemplate = (point, destinations, offers)=> { +const createEventEditorTemplate = (point, offers, destinations)=> { const eventDestination = destinations.find((item) => item.id === point.destination); const defaultOffers = offers.find((offer) => offer.type === point.type).offers; const selectedOffers = defaultOffers.filter((defaultOffer) => point.offers.includes(defaultOffer.id)); @@ -137,31 +137,32 @@ const createEventEditorTemplate = (point, destinations, offers)=> { }; export default class EventEditorView extends AbstractView { - #point = null; - #destinations = null; - #offers = null; + #point = []; + #destinations = []; + #offers = []; + #handleFormSubmit = null; #handleEditClick = null; - constructor({point, destinations, offers, onFormSubmit, onEditClick}) { + constructor({ point, offers, destinations, onEditClick, onFormSubmit }) { super(); this.#point = point; - this.#destinations = destinations; this.#offers = offers; - this.#handleFormSubmit = onFormSubmit; + this.#destinations = destinations; this.#handleEditClick = onEditClick; + this.#handleFormSubmit = onFormSubmit; this.element.querySelector('form').addEventListener('submit', this.#formSubmitHandler); this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler); } get template() { - return createEventEditorTemplate(this.#point, this.#destinations, this.#offers); + return createEventEditorTemplate(this.#point, this.#offers, this.#destinations); } #formSubmitHandler = (event) => { event.preventDefault(); - this.#handleFormSubmit(); + this.#handleFormSubmit(this.#point); }; #editClickHandler = (event) => { diff --git a/src/view/events-item-view.js b/src/view/events-item-view.js index 37db681..2fd7094 100644 --- a/src/view/events-item-view.js +++ b/src/view/events-item-view.js @@ -18,7 +18,7 @@ const createEventSelectedOffersTemplate = (selectedOffers) => { `; }; -const createEventItemTemplate = (point, destinations, offers) => { +const createEventItemTemplate = (point, offers, destinations) => { const { basePrice, dateFrom, dateTo, isFavorite, type } = point; const defaultOffers = offers.find((offer) => offer.type === point.type).offers; @@ -66,27 +66,36 @@ const createEventItemTemplate = (point, destinations, offers) => { }; export default class EventsItemView extends AbstractView { - #point = null; - #destinations = null; - #offers = null; + #point = []; + #destinations = []; + #offers = []; + #handleEditClick = null; + #handleFavoriteClick = null; - constructor({point, destinations, offers, onEditClick}) { + constructor({ point, offers, destinations, onEditClick, onFavoriteClick }) { super(); this.#point = point; this.#destinations = destinations; this.#offers = offers; this.#handleEditClick = onEditClick; + this.#handleFavoriteClick = onFavoriteClick; this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler); + this.element.querySelector('.event__favorite-btn').addEventListener('click', this.#favoriteClickHandler); } get template() { - return createEventItemTemplate(this.#point, this.#destinations, this.#offers); + return createEventItemTemplate(this.#point, this.#offers, this.#destinations); } #editClickHandler = (event) => { event.preventDefault(); this.#handleEditClick(); }; + + #favoriteClickHandler = (event) => { + event.preventDefault(); + this.#handleFavoriteClick(); + }; }