From c946d06ef5db325787ae2a7549ad8ee2d9b730e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B0=D1=81=D1=82=D0=B0=D1=81=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=A2=D0=BE=D0=BF=D0=BE=D1=80=D0=BA=D0=BE=D0=B2=D0=B0?= Date: Tue, 28 May 2024 01:24:11 +0500 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D1=8B=20(=D1=87?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=8C=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/const.js | 99 +++++++++++++----------- src/presenter/point-presenter.js | 117 +++++++++++++++++++++++++++++ src/presenter/trip-presenter.js | 122 ++++++++++++++---------------- src/utils.js | 125 ++++++++++++++++--------------- src/view/point-view.js | 88 ++++++++++++---------- 5 files changed, 344 insertions(+), 207 deletions(-) create mode 100644 src/presenter/point-presenter.js diff --git a/src/const.js b/src/const.js index 414d7d5..b9415f0 100644 --- a/src/const.js +++ b/src/const.js @@ -18,64 +18,79 @@ const CITIES = [ ]; const OFFERS = [ - 'Order Uber', - 'Add luggage', - 'Switch to comfort', - 'Rent a car', - 'Add breakfast', - 'Book tickets', - 'Lunch in city', - 'Upgrade to a business class' + 'Order Uber', + 'Add luggage', + 'Switch to comfort', + 'Rent a car', + 'Add breakfast', + 'Book tickets', + 'Lunch in city', + 'Upgrade to a business class' ]; const DESCRIPTION = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras aliquet varius magna, non porta ligula feugiat eget. Fusce tristique felis at fermentum pharetra. Aliquam id orci ut lectus varius viverra.'; const Price = { - MIN: 1, - MAX: 1000 + MIN: 1, + MAX: 1000 }; const TYPES = [ - 'taxi', - 'bus', - 'train', - 'ship', - 'drive', - 'flight', - 'check-in', - 'sightseeing', - 'restaurant' + 'taxi', + 'bus', + 'train', + 'ship', + 'drive', + 'flight', + 'check-in', + 'sightseeing', + 'restaurant' ]; const DEFAULT_TYPE = 'flight'; const POINT_EMPTY = { - basePrice: 0, - dateFrom: null, - dateTo: null, - destination: null, - isFavorite: false, - offers: [], - type: DEFAULT_TYPE + basePrice: 0, + dateFrom: null, + dateTo: null, + destination: null, + isFavorite: false, + offers: [], + type: DEFAULT_TYPE }; const FilterType = { - EVERYTHING: 'everything', - FUTURE: 'future', - PRESENT: 'present', - PAST: 'past' + EVERYTHING: 'everything', + FUTURE: 'future', + PRESENT: 'present', + PAST: 'past' +} + +const Mode = { + DEFAULT: 'default', + EDITING: 'editing', }; -export { - OFFER_COUNT, - DESTINATION_COUNT, - POINT_COUNT, - CITIES, - OFFERS, - DESCRIPTION, - Price, - TYPES, - DEFAULT_TYPE, - POINT_EMPTY, - FilterType +const SortType = { + DAY: 'day', + EVENT: 'event', + TIME: 'time', + PRICE: 'price', + OFFERS: 'offers' }; + +export { + OFFER_COUNT, + DESTINATION_COUNT, + POINT_COUNT, + CITIES, + OFFERS, + DESCRIPTION, + Price, + TYPES, + DEFAULT_TYPE, + POINT_EMPTY, + FilterType, + Mode, + SortType +} diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js new file mode 100644 index 0000000..d745edf --- /dev/null +++ b/src/presenter/point-presenter.js @@ -0,0 +1,117 @@ +import PointView from '../view/point-view.js'; +import EditPointView from '../view/edit-point-view.js'; +import { remove, render, replace } from '../framework/render.js'; +import { Mode } from '../const.js'; + +export default class PointPresenter { + #container = null; + + #destinationsModel = null; + #offersModel = null; + + #handleDataChange = null; + #handleModeChange = null; + + #pointComponent = null; + #editPointComponent = null; + #point = null; + #mode = Mode.DEFAULT; + + constructor({container, destinationsModel, offersModel, onDataChange, onModeChange}) { + this.#container = container; + this.#destinationsModel = destinationsModel; + this.#offersModel = offersModel; + this.#handleDataChange = onDataChange; + this.#handleModeChange = onModeChange; + } + + init(point) { + this.#point = point; + + const prevPointComponent = this.#pointComponent; + const prevEditPointComponent = this.#editPointComponent; + + this.#pointComponent = new PointView({ + point: this.#point, + pointDestination: this.#destinationsModel.getById(point.destination), + pointOffers: this.#offersModel.getByType(point.type), + onEditClick: this.#pointEditClickHandler, + onFavoriteClick: this.#favoriteClickHandler + }); + + this.#editPointComponent = new EditPointView({ + point: this.#point, + pointDestination: this.#destinationsModel.getById(point.destination), + pointOffers: this.#offersModel.getByType(point.type), + onSubmitClick: this.#formSubmitHandler, + onResetClick: this.#resetButtonClickHandler + }) + + if (prevPointComponent === null || prevEditPointComponent === null) { + render(this.#pointComponent, this.#container); + return; + } + + if (this.#mode === Mode.DEFAULT) { + replace(this.#pointComponent, prevPointComponent); + } + + if (this.#mode === Mode.EDITING) { + replace(this.#pointComponent, prevEditPointComponent); + } + + remove(prevPointComponent); + remove(prevEditPointComponent); + } + + resetView = () => { + if (this.#mode !== Mode.DEFAULT) { + this.#replaceFormToPoint(); + } + }; + + destroy = () => { + remove(this.#pointComponent); + remove(this.#editPointComponent); + } + + #replacePointToForm = () => { + replace(this.#editPointComponent, this.#pointComponent); + document.addEventListener('keydown', this.#escKeyDownHandler); + this.#handleModeChange(); + this.#mode = Mode.EDITING; + }; + + #replaceFormToPoint = () => { + replace(this.#pointComponent, this.#editPointComponent); + document.removeEventListener('keydown', this.#escKeyDownHandler); + this.#mode = Mode.DEFAULT; + }; + + #escKeyDownHandler = (evt) => { + if (evt.key === 'Escape') { + evt.preventDefault(); + this.#replaceFormToPoint(); + } + }; + + #pointEditClickHandler = () => { + this.#replacePointToForm(); + }; + + #favoriteClickHandler = () => { + this.#handleDataChange({ + ...this.#point, + isFavorite: !this.#point.isFavorite + }); + }; + + #formSubmitHandler = (point) => { + this.#handleDataChange(point); + this.#replaceFormToPoint(); + } + + #resetButtonClickHandler = () => { + this.#replaceFormToPoint(); + } +} diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index 9dcecbe..e0a751e 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -1,89 +1,81 @@ import SortView from '../view/sort-view.js'; -import EditPointView from '../view/edit-point-view.js'; -import PointView from '../view/point-view.js'; import TripView from '../view/point-list-view.js'; import EmptyListView from '../view/empty-list-view.js'; +import PointPresenter from './point-presenter.js'; import { render, replace } from '../framework/render.js'; +import { updateItem } from '../utils.js'; +import PointListView from '../view/point-list-view.js'; export default class TripPresenter { - #tripContainer = null; - #destinationsModel = null; - #offersModel = null; - #pointsModel = null; - #pointListComponent = new TripView(); - #sortComponent = new SortView(); + #tripContainer = null; + #destinationsModel = null; + #offersModel = null; + #pointsModel = null; + #pointListComponent = new TripView(); + #sortComponent = new SortView(); - #points = []; + #points = []; - constructor({ tripContainer, destinationsModel, offersModel, pointsModel }) { - this.#tripContainer = tripContainer; - this.#destinationsModel = destinationsModel; - this.#offersModel = offersModel; - this.#pointsModel = pointsModel; - this.#points = [...this.#pointsModel.get()]; - } + #pointPresenters = new Map(); - init() { - if (this.#points.length === 0) { - render(new EmptyListView(), this.#tripContainer); - return; + constructor({tripContainer, destinationsModel, offersModel, pointsModel}) { + this.#tripContainer = tripContainer; + this.#destinationsModel = destinationsModel; + this.#offersModel = offersModel; + this.#pointsModel = pointsModel; + this.#points = [...this.#pointsModel.get()]; } - render(this.#sortComponent, this.#tripContainer); - render(this.#pointListComponent, this.#tripContainer); - - this.#points.forEach((point) => { - this.#renderPoint(point); - }); - } + init() { + this.#renderBoard(); + } - #renderPoint = (point) => { - const pointComponent = new PointView({ - point, - pointDestination: this.#destinationsModel.getById(point.destination), - pointOffers: this.#offersModel.getByType(point.type), - onEditClick: pointEditClickHandler - }); + #renderPoint = (point) => { + const pointPresenter = new PointPresenter({ + container: this.#pointListComponent.element, + destinationsModel: this.#destinationsModel, + offersModel: this.#offersModel, + onDataChange: this.#pointChangeHandler, + onModeChange: this.#modeChangeHandler + }); - const editPointComponent = new EditPointView({ - point, - pointDestination: this.#destinationsModel.getById(point.destination), - pointOffers: this.#offersModel.getByType(point.type), - onSubmitClick: pointSubmitHandler, - onResetClick: resetButtonClickHandler - }); + pointPresenter.init(point); - const replacePointToForm = () => { - replace(editPointComponent, pointComponent); - }; - - const replaceFormToPoint = () => { - replace(pointComponent, editPointComponent); - }; + this.#pointPresenters.set(point.id, pointPresenter); + } - const escKeyDownHandler = (evt) => { - if (evt.key === 'Escape' || evt.key === 'Esc') { - evt.preventDefault(); - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); - } + #renderPoints = () => { + this.#points.forEach((point) => { + this.#renderPoint(point); + }); }; - function pointEditClickHandler() { - replacePointToForm(); - document.addEventListener('keydown', escKeyDownHandler); + #clearPoints = () => { + this.#pointPresenters.forEach((presenter) => presenter.destroy()); + this.#pointPresenters.clear(); } - function resetButtonClickHandler() { - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); + #renderPointContainer = () => { + this.#pointListComponent = new PointListView(); + render(this.#pointListComponent, this.#tripContainer); } - function pointSubmitHandler() { - replaceFormToPoint(); - document.removeEventListener('keydown', escKeyDownHandler); + #renderBoard = () => { + if (this.#points.length === 0) { + render(new EmptyListView(), this.#tripContainer); + return; + } + + this.#renderPointContainer(); + this.#renderPoints(); + }; + + #pointChangeHandler = (updatedPoint) => { + this.#points = updateItem(this.#points, updatedPoint); + this.#pointPresenters.get(updatedPoint.id).init(updatedPoint); } - render(pointComponent, this.#pointListComponent.element); - }; + #modeChangeHandler = () => { + this.#pointPresenters.forEach((presenter) => presenter.resetView()); + }; } diff --git a/src/utils.js b/src/utils.js index 54db7ef..a0d831f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,108 +6,113 @@ dayjs.extend(duration); dayjs.extend(relativeTime); const TimePeriods = { - MSEC_IN_SEC: 1000, - SEC_IN_MIN: 60, - MIN_IN_HOUR: 60, - HOUR_IN_DAY: 24 -}; + MSEC_IN_SEC: 1000, + SEC_IN_MIN: 60, + MIN_IN_HOUR: 60, + HOUR_IN_DAY: 24 +} const MSEC_IN_HOUR = TimePeriods.MIN_IN_HOUR * TimePeriods.SEC_IN_MIN * TimePeriods.MSEC_IN_SEC; const MSEC_IN_DAY = TimePeriods.HOUR_IN_DAY * MSEC_IN_HOUR; const Duration = { - HOUR: 5, - DAY: 5, - MIN: 59 + HOUR: 5, + DAY: 5, + MIN: 59 }; let date = dayjs().subtract(getRandomInteger(0, Duration.DAY), 'day').toDate(); -function getDate({ next }) { - const minsGap = getRandomInteger(0, Duration.MIN); - const hoursGap = getRandomInteger(1, Duration.HOUR); - const daysGap = getRandomInteger(0, Duration.DAY); +function getDate({next}) { + const minsGap = getRandomInteger(0, Duration.MIN); + const hoursGap = getRandomInteger(1, Duration.HOUR); + const daysGap = getRandomInteger(0, Duration.DAY); - if (next) { - date = dayjs(date) - .add(minsGap, 'minute') - .add(hoursGap, 'hour') - .add(daysGap, 'day') - .toDate(); - } + if (next) { + date = dayjs(date) + .add(minsGap, 'minute') + .add(hoursGap, 'hour') + .add(daysGap, 'day') + .toDate(); + } - return date; + return date; } function getRandomInteger(a = 0, b = 1) { - const lower = Math.ceil(Math.min(a, b)); - const upper = Math.floor(Math.max(a, b)); + const lower = Math.ceil(Math.min(a,b)); + const upper = Math.floor(Math.max(a,b)); - return Math.floor(lower + Math.random() * (upper - lower + 1)); + return Math.floor(lower + Math.random() * (upper - lower + 1)); } function getRandomValue(items) { - return items[getRandomInteger(0, items.length - 1)]; + return items[getRandomInteger(0, items.length - 1)]; } function formatStringToDateTime(date) { - return dayjs(date).format('DD/MM/YY HH:mm'); + return dayjs(date).format('DD/MM/YY HH:mm'); } function formatStringToShortDate(date) { - return dayjs(date).format('MMM DD'); + return dayjs(date).format('MMM DD'); } function formatStringToTime(date) { - return dayjs(date).format('HH:mm'); + return dayjs(date).format('HH:mm'); } function capitalize(string) { - return `${string[0].toUpperCase()}${string.slice(1)}`; + return `${string[0].toUpperCase()}${string.slice(1)}`; } function getPointDuration(dateFrom, dateTo) { - const timeDiff = dayjs(dateTo).diff(dayjs(dateFrom)); - - let pointDuration = 0; - - switch (true) { - case (timeDiff >= MSEC_IN_DAY): - pointDuration = dayjs.duration(timeDiff).format('DD[D] HH[H] mm[M]'); - break; - case (timeDiff >= MSEC_IN_HOUR): - pointDuration = dayjs.duration(timeDiff).format('HH[H] mm[M]'); - break; - case (timeDiff < MSEC_IN_HOUR): - pointDuration = dayjs.duration(timeDiff).format('mm[M]'); - break; - } - - return pointDuration; + const timeDiff = dayjs(dateTo).diff(dayjs(dateFrom)); + + let pointDuration = 0; + + switch (true) { + case (timeDiff >= MSEC_IN_DAY): + pointDuration = dayjs.duration(timeDiff).format('DD[D] HH[H] mm[M]'); + break; + case (timeDiff >= MSEC_IN_HOUR): + pointDuration = dayjs.duration(timeDiff).format('HH[H] mm[M]'); + break; + case (timeDiff < MSEC_IN_HOUR): + pointDuration = dayjs.duration(timeDiff).format('mm[M]'); + break; + } + + return pointDuration; } function isPointFuture(point) { - return dayjs().isBefore(point.dateFrom); + return dayjs().isBefore(point.dateFrom); } function isPointPresent(point) { - return (dayjs().isAfter(point.dateFrom) && dayjs().isBefore(point.dateTo)); + return (dayjs().isAfter(point.dateFrom) && dayjs().isBefore(point.dateTo)); } function isPointPast(point) { - return dayjs().isAfter(point.dateTo); + return dayjs().isAfter(point.dateTo); +} + +function updateItem(items, update) { + return items.map((item) => item.id === update.id ? update : item); } export { - getDate, - getRandomInteger, - getRandomValue, - formatStringToDateTime, - formatStringToShortDate, - formatStringToTime, - capitalize, - getPointDuration, - isPointFuture, - isPointPast, - isPointPresent -}; + getDate, + getRandomInteger, + getRandomValue, + formatStringToDateTime, + formatStringToShortDate, + formatStringToTime, + capitalize, + getPointDuration, + isPointFuture, + isPointPast, + isPointPresent, + updateItem +} diff --git a/src/view/point-view.js b/src/view/point-view.js index e5b3ae3..46cb207 100644 --- a/src/view/point-view.js +++ b/src/view/point-view.js @@ -1,26 +1,26 @@ import AbstractView from '../framework/view/abstract-view.js'; -import { formatStringToDateTime, formatStringToShortDate, formatStringToTime, getPointDuration } from '../utils.js'; +import { formatStringToDateTime, formatStringToShortDate, formatStringToTime, getPointDuration} from '../utils.js'; -const createPointOffersTemplate = ({ pointOffers }) => { - const offerItems = pointOffers.map(offer => { - return ( - `
  • +const createPointOffersTemplate = ({pointOffers}) => { + const offerItems = pointOffers.map(offer => { + return ( + `
  • ${offer.title} +€  ${offer.price}
  • ` - ); - }).join(''); + ); + }).join(''); - return ``; -}; + return ``; +} -const createPointTemplate = ({ point, pointDestination, pointOffers }) => { - const { basePrice, dateFrom, dateTo, offers, isFavorite, type } = point; - const favoriteClassName = isFavorite ? 'event__favorite-btn--active' : ''; +const createPointTemplate = ({point, pointDestination, pointOffers}) => { + const { basePrice, dateFrom, dateTo, offers, isFavorite, type } = point; + const favoriteClassName = isFavorite ? 'event__favorite-btn--active' : ''; - return ( - `
  • + return ( + `
  • @@ -39,7 +39,7 @@ const createPointTemplate = ({ point, pointDestination, pointOffers }) => { € ${basePrice}

    Offers:

    - ${createPointOffersTemplate({ pointOffers })} + ${createPointOffersTemplate({pointOffers})}
  • ` - ); -}; + ); +} export default class PointView extends AbstractView { - #point = null; - #pointDestination = null; - #pointOffers = null; - #onEditClick = null; + #point = null; + #pointDestination = null; + #pointOffers = null; + #onEditClick = null; + #onFavoriteClick = null; + + constructor({point, pointDestination, pointOffers, onEditClick, onFavoriteClick}) { + super(); + this.#point = point; + this.#pointDestination = pointDestination; + this.#pointOffers = pointOffers; + this.#onEditClick = onEditClick; + this.#onFavoriteClick = onFavoriteClick; - constructor({ point, pointDestination, pointOffers, onEditClick }) { - super(); - this.#point = point; - this.#pointDestination = pointDestination; - this.#pointOffers = pointOffers; - this.#onEditClick = onEditClick; + this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler); + this.element.querySelector('.event__favorite-icon').addEventListener('click', this.#favoriteClickHandler); + } - this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler); - } + get template() { + return createPointTemplate({ + point: this.#point, + pointDestination: this.#pointDestination, + pointOffers: this.#pointOffers + }); + } - get template() { - return createPointTemplate({ - point: this.#point, - pointDestination: this.#pointDestination, - pointOffers: this.#pointOffers - }); - } + #editClickHandler = (evt) => { + evt.preventDefault(); + this.#onEditClick(); + } - #editClickHandler = (evt) => { - evt.preventDefault(); - this.#onEditClick(); - }; + #favoriteClickHandler = (evt) => { + evt.preventDefault(); + this.#onFavoriteClick(); + } }