From 1e9ba5303e06b97fd4f2844b62929c2542ec634f Mon Sep 17 00:00:00 2001 From: Danilina Mariya Date: Mon, 30 Sep 2024 12:21:29 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D1=83?= =?UTF-8?q?=D0=B5=D1=82=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8E,=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B8=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=82=D0=BE=D1=87=D0=B5=D0=BA=20=D0=BC=D0=B0=D1=80?= =?UTF-8?q?=D1=88=D1=80=D1=83=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- package.json | 1 + public/index.html | 2 +- src/const.js | 14 ++- src/main.js | 38 +++++- src/model/filter-model.js | 15 +++ src/model/points-model.js | 43 ++++++- src/presenter/board-presenter.js | 171 ++++++++++++++++++--------- src/presenter/filter-presenter.js | 60 ++++++++++ src/presenter/new-point-presenter.js | 70 +++++++++++ src/presenter/point-presenter.js | 47 ++++++-- src/utils/common.js | 6 +- src/utils/points.js | 6 +- src/view/filters-view.js | 27 ++++- src/view/form-point-view.js | 44 +++++-- src/view/new-point-button-view.js | 24 ++++ src/view/no-point-view.js | 12 +- src/view/sort-view.js | 2 + 18 files changed, 475 insertions(+), 109 deletions(-) create mode 100644 src/model/filter-model.js create mode 100644 src/presenter/filter-presenter.js create mode 100644 src/presenter/new-point-presenter.js create mode 100644 src/view/new-point-button-view.js diff --git a/package-lock.json b/package-lock.json index ed83f69..9516d66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "dayjs": "1.11.7", "flatpickr": "4.6.13", + "he": "1.2.0", "nanoid": "5.0.3" }, "devDependencies": { @@ -4601,7 +4602,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, "bin": { "he": "bin/he" } diff --git a/package.json b/package.json index 7a16eb7..136a5c4 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "dayjs": "1.11.7", "flatpickr": "4.6.13", + "he": "1.2.0", "nanoid": "5.0.3" } } diff --git a/public/index.html b/public/index.html index 44c26b4..5297837 100644 --- a/public/index.html +++ b/public/index.html @@ -22,7 +22,7 @@

Filter events

- + diff --git a/src/const.js b/src/const.js index f615cd4..2993be9 100644 --- a/src/const.js +++ b/src/const.js @@ -20,5 +20,17 @@ const SortType = { OFFER: 'offer' }; +const UserAction = { + UPDATE_POINT: 'UPDATE_POINT', + ADD_POINT: 'ADD_POINT', + DELETE_POINT: 'DELETE_POINT', +}; + +const UpdateType = { + PATCH: 'PATCH', + MINOR: 'MINOR', + MAJOR: 'MAJOR', +}; + -export {NO_POINT_MESSAGES, FiltersValues, SortType}; +export {NO_POINT_MESSAGES, FiltersValues, SortType, UserAction, UpdateType}; diff --git a/src/main.js b/src/main.js index 280377d..dd3d69c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,17 +1,43 @@ -import FiltersView from './view/filters-view.js'; -import { render } from './framework/render.js'; import BoardPresenter from './presenter/board-presenter.js'; +import FilterPresenter from './presenter/filter-presenter.js'; import PointsModel from './model/points-model.js'; -import {generateFilter} from './mock/filter.js'; +import FilterModel from './model/filter-model.js'; +import NewPointButtonView from './view/new-point-button-view.js'; +import { render, RenderPosition } from './framework/render.js'; const header = document.querySelector('.trip-main'); const filtersContainer = header.querySelector('.trip-controls__filters'); const main = document.querySelector('.page-main'); const mainContainer = main.querySelector('.trip-events'); const pointsModel = new PointsModel(); -const boardPresenter = new BoardPresenter({boardContainer: mainContainer, pointsModel}); -const filters = generateFilter(pointsModel.points); +const filterModel = new FilterModel(); -render(new FiltersView({filters}), filtersContainer); +const boardPresenter = new BoardPresenter({ + boardContainer: mainContainer, + pointsModel, + filterModel, + onNewPointDestroy: handleNewPointFormClose}); +const filterPresenter = new FilterPresenter({ + filterContainer: filtersContainer, + filterModel: filterModel, + pointsModel: pointsModel +}); + +const newPointButtonComponent = new NewPointButtonView({ + onClick: handleNewPointButtonClick +}); + +function handleNewPointFormClose() { + newPointButtonComponent.element.disabled = false; +} + +function handleNewPointButtonClick() { + boardPresenter.createPoint(); + newPointButtonComponent.element.disabled = true; +} + +render(newPointButtonComponent, header, RenderPosition.AFTEREND); + +filterPresenter.init(); boardPresenter.init(); diff --git a/src/model/filter-model.js b/src/model/filter-model.js new file mode 100644 index 0000000..fb6017f --- /dev/null +++ b/src/model/filter-model.js @@ -0,0 +1,15 @@ +import Observable from '../framework/observable.js'; +import {FiltersValues} from '../const.js'; + +export default class FilterModel extends Observable { + #filter = FiltersValues.EVERYTHING; + + get filter() { + return this.#filter; + } + + setFilter(updateType, filter) { + this.#filter = filter; + this._notify(updateType, filter); + } +} diff --git a/src/model/points-model.js b/src/model/points-model.js index cbe12f2..9edc690 100644 --- a/src/model/points-model.js +++ b/src/model/points-model.js @@ -1,10 +1,11 @@ import { getRandomPoint } from '../mock/points'; +import Observable from '../framework/observable.js'; import { getDestinations } from '../mock/destinations'; import { getOffers } from '../mock/offers'; const POINT_COUNT = 4; -export default class PointsModel { +export default class PointsModel extends Observable{ #allOffers = getOffers(); #allDestinations = getDestinations(); #points = Array.from({length: POINT_COUNT}, getRandomPoint); @@ -21,5 +22,45 @@ export default class PointsModel { return this.#allOffers; } + updatePoint(updateType, update) { + const index = this.#points.findIndex((point) => point.id === update.id); + + if (index === -1) { + throw new Error('Can\'t update unexisting Point'); + } + + this.#points = [ + ...this.#points.slice(0, index), + update, + ...this.#points.slice(index + 1), + ]; + + this._notify(updateType, update); + } + + addPoint(updateType, update) { + this.#points = [ + update, + ...this.#points, + ]; + + this._notify(updateType, update); + } + + deletePoint(updateType, update) { + const index = this.#points.findIndex((point) => point.id === update.id); + + if (index === -1) { + throw new Error('Can\'t delete unexisting Point'); + } + + this.#points = [ + ...this.#points.slice(0, index), + ...this.#points.slice(index + 1), + ]; + + this._notify(updateType); + } + } diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 4178487..56dcbbd 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -2,50 +2,111 @@ import SortView from '../view/sort-view.js'; import ListPointsView from '../view/list-points-view.js'; import NoPointView from '../view/no-point-view.js'; import PointPresenter from './point-presenter.js'; +import NewPointPresenter from './new-point-presenter.js'; +import {filter} from '../utils/filter.js'; import { render, remove } from '../framework/render.js'; -import {updateItem} from '../utils/common.js'; import { sortPointDate, sortPointPrice, sortPointTime } from '../utils/points.js'; -import { SortType, FiltersValues } from '../const.js'; +import { SortType, UpdateType, UserAction, FiltersValues } from '../const.js'; export default class BoardPresenter { #boardContainer = null; #pointsModel = null; + #filterModel = null; #listContainer = new ListPointsView(); - #boardPoints = []; #sortComponent = null; #noPointComponent = null; #pointPresenters = new Map(); - #newPointPresenters = null; + #newPointPresenter = null; #currentSortType = SortType.DAY; + #filterType = FiltersValues.EVERYTHING; - constructor({boardContainer, pointsModel}) { + constructor({boardContainer, pointsModel, filterModel, onNewPointDestroy}) { this.#boardContainer = boardContainer; this.#pointsModel = pointsModel; + this.#filterModel = filterModel; + + this.#pointsModel.addObserver(this.#handleModelEvent); + this.#filterModel.addObserver(this.#handleModelEvent); + + this.#newPointPresenter = new NewPointPresenter({ + allOffers: this.offers, + allDestinations: this.destinations, + pointListContainer: this.#listContainer.element, + onDataChange: this.#handleViewAction, + onDestroy: onNewPointDestroy + }); + } + + get points() { + this.#filterType = this.#filterModel.filter; + const points = this.#pointsModel.points; + + const filteredPoints = filter[this.#filterType](points); + + switch (this.#currentSortType) { + case SortType.TIME: + return filteredPoints.sort(sortPointTime); + case SortType.PRICE: + return filteredPoints.sort(sortPointPrice); + } + return filteredPoints.sort(sortPointDate); + } + + get destinations() { + return this.#pointsModel.destinations; + } + + get offers() { + return this.#pointsModel.offers; } init() { - this.#boardPoints = [...this.#pointsModel.points]; - this.allOffers = [...this.#pointsModel.offers]; - this.allDestinations = [...this.#pointsModel.destinations]; - this.#boardPoints.sort(sortPointDate); this.#renderBoard(); } - #renderSort(sortType) { + createPoint() { + this.#currentSortType = SortType.DEFAULT; + this.#filterModel.setFilter(UpdateType.MAJOR, FiltersValues.EVERYTHING); + this.#newPointPresenter.init(); + } + + #renderBoard() { + const filterType = this.#filterModel.filter; + if (this.points.length === 0) { + this.#renderNoPoint(filterType); + return; + } + this.#renderSort(); + this.#renderPointsList(); + } + + #renderNoPoint() { + this.#noPointComponent = new NoPointView({filterType: this.#filterType}); + render(this.#noPointComponent, this.#boardContainer); + } + + #renderSort() { this.#sortComponent = new SortView({ onSortTypeChange: this.#handleSortTypeChange, - sortType + sortType: this.#currentSortType }); render(this.#sortComponent, this.#boardContainer); } + #renderPointsList() { + render(this.#listContainer, this.#boardContainer); + this.points.forEach((point) => { + this.#renderPoint(point, this.offers, this.destinations); + }); + } + #renderPoint(point, allOffers, allDestinations) { const pointPresenter = new PointPresenter({ pointsListContainer: this.#listContainer.element, - onDataChange: this.#handlePointChange, + onDataChange: this.#handleViewAction, onModeChange: this.#handleModeChange }); @@ -53,71 +114,67 @@ export default class BoardPresenter { this.#pointPresenters.set(point.id, pointPresenter); } - #renderPointsList() { - render(this.#listContainer, this.#boardContainer); - this.#boardPoints.forEach((point) => { - this.#renderPoint(point, this.allOffers, this.allDestinations); - }); - } - - #renderNoPoint(filter) { - this.#noPointComponent = new NoPointView(filter); - render(this.#noPointComponent, this.#boardContainer); - } - - - #renderBoard() { - if (this.#boardPoints.length === 0) { - this.#renderNoPoint(FiltersValues.EVERYTHING); - return; - } - this.#renderSort(this.#currentSortType); - this.#renderPointsList(); - } - - #clearPointsList() { + #clearBoard({resetSortType = false} = {}) { + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.destroy()); this.#pointPresenters.clear(); - } - #sortPoints = (sortType) => { - switch (sortType) { - case SortType.DAY: - this.#boardPoints.sort(sortPointDate); - break; - case SortType.TIME: - this.#boardPoints.sort(sortPointTime); - break; - case SortType.PRICE: - this.#boardPoints.sort(sortPointPrice); - break; + remove(this.#sortComponent); + + if (resetSortType) { + this.#currentSortType = SortType.DAY; } - this.#currentSortType = sortType; - }; + if (this.#noPointComponent) { + remove(this.#noPointComponent); + } + } #handleSortTypeChange = (sortType) => { if (this.#currentSortType === sortType) { return; } + this.#currentSortType = sortType; remove(this.#sortComponent); this.#renderSort(sortType); - this.#sortPoints(sortType); - this.#clearPointsList(); - this.#renderPointsList(); - + this.#clearBoard(); + this.#renderBoard(); }; #handleModeChange = () => { + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.resetView()); }; - - #handlePointChange = (updatedPoint) => { - this.#boardPoints = updateItem(this.#boardPoints, updatedPoint); - this.#pointPresenters.get(updatedPoint.id).init(updatedPoint, this.allOffers, this.allDestinations); + #handleViewAction = (actionType, updateType, update) => { + switch (actionType) { + case UserAction.UPDATE_POINT: + this.#pointsModel.updatePoint(updateType, update); + break; + case UserAction.ADD_POINT: + this.#pointsModel.addPoint(updateType, update); + break; + case UserAction.DELETE_POINT: + this.#pointsModel.deletePoint(updateType, update); + break; + } }; + #handleModelEvent = (updateType, data) => { + switch (updateType) { + case UpdateType.PATCH: + this.#pointPresenters.get(data.id).init(data, this.offers, this.destinations); + break; + case UpdateType.MINOR: + this.#clearBoard(); + this.#renderBoard(); + break; + case UpdateType.MAJOR: + this.#clearBoard({resetSortType: true}); + this.#renderBoard(); + break; + } + }; } diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js new file mode 100644 index 0000000..b2fe8a5 --- /dev/null +++ b/src/presenter/filter-presenter.js @@ -0,0 +1,60 @@ +import {render, replace, remove} from '../framework/render.js'; +import FiltersView from '../view/filters-view.js'; +import {filter} from '../utils/filter.js'; +import {FiltersValues, UpdateType} from '../const.js'; + + +export default class FilterPresenter { + #filterContainer = null; + #filterModel = null; + #pointsModel = null; + + #filterComponent = null; + + constructor({filterContainer, filterModel, pointsModel}) { + this.#filterContainer = filterContainer; + this.#filterModel = filterModel; + this.#pointsModel = pointsModel; + this.#pointsModel.addObserver(this.#handleModelEvent); + this.#filterModel.addObserver(this.#handleModelEvent); + } + + get filters() { + const points = this.#pointsModel.points; + return Object.values(FiltersValues).map((type) => ({ + type, + points: filter[type](points) + })); + } + + init() { + const filters = this.filters; + const prevFilterComponent = this.#filterComponent; + + this.#filterComponent = new FiltersView({ + filters, + currentFilterType: this.#filterModel.filter, + onFilterTypeChange: this.#handleFilterTypeChange + }); + + if (prevFilterComponent === null) { + render(this.#filterComponent, this.#filterContainer); + return; + } + + replace(this.#filterComponent, prevFilterComponent); + remove(prevFilterComponent); + } + + #handleModelEvent = () => { + this.init(); + }; + + #handleFilterTypeChange = (filterType) => { + if (this.#filterModel.filter === filterType) { + return; + } + + this.#filterModel.setFilter(UpdateType.MAJOR, filterType); + }; +} diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js new file mode 100644 index 0000000..00daed8 --- /dev/null +++ b/src/presenter/new-point-presenter.js @@ -0,0 +1,70 @@ +import {remove, render, RenderPosition} from '../framework/render.js'; +import FormPointView from '../view/form-point-view.js'; +import {nanoid} from 'nanoid'; +import {UserAction, UpdateType} from '../const.js'; + +export default class NewTaskPresenter { + #pointListContainer = null; + #handleDataChange = null; + #handleDestroy = null; + + #pointEditComponent = null; + + constructor({allOffers, allDestinations, pointListContainer, onDataChange, onDestroy}) { + this.#pointListContainer = pointListContainer; + this.#handleDataChange = onDataChange; + this.#handleDestroy = onDestroy; + this.allOffers = allOffers; + this.allDestinations = allDestinations; + } + + init() { + if (this.#pointEditComponent !== null) { + return; + } + + this.#pointEditComponent = new FormPointView({ + allOffers: this.allOffers, + allDestinations: this.allDestinations, + onFormSubmit: this.#handleFormSubmit, + onDeleteClick: this.#handleDeleteClick + }); + + render(this.#pointEditComponent, this.#pointListContainer, RenderPosition.AFTERBEGIN); + + document.addEventListener('keydown', this.#escKeyDownHandler); + } + + destroy() { + if (this.#pointEditComponent === null) { + return; + } + + this.#handleDestroy(); + + remove(this.#pointEditComponent); + this.#pointEditComponent = null; + + document.removeEventListener('keydown', this.#escKeyDownHandler); + } + + #handleFormSubmit = (task) => { + this.#handleDataChange( + UserAction.ADD_POINT, + UpdateType.MINOR, + {id: nanoid(), ...task}, + ); + this.destroy(); + }; + + #handleDeleteClick = () => { + this.destroy(); + }; + + #escKeyDownHandler = (evt) => { + if (evt.key === 'Escape' || evt.key === 'Esc') { + evt.preventDefault(); + this.destroy(); + } + }; +} diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 2194f88..0ba8112 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -1,6 +1,8 @@ import {render, replace, remove} from '../framework/render.js'; import PointView from '../view/point-view.js'; import FormPointView from '../view/form-point-view.js'; +import {UserAction, UpdateType} from '../const.js'; +import { isDatesEqual } from '../utils/points.js'; const Mode = { DEFAULT: 'DEFAULT', @@ -30,6 +32,7 @@ export default class PointPresenter { const prevPointComponent = this.#pointComponent; const prevPointEditComponent = this.#pointEditComponent; + this.#pointComponent = new PointView({ point: this.#point, allOffers: allOffers, @@ -42,7 +45,8 @@ export default class PointPresenter { point: this.#point, allOffers: allOffers, allDestinations: allDestinations, - onFormSubmit: this.#handleFormSubmit + onFormSubmit: this.#handleFormSubmit, + onDeleteClick: this.#handleDeleteClick }); if (prevPointComponent === null || prevPointEditComponent === null) { @@ -74,15 +78,6 @@ export default class PointPresenter { remove(this.#pointEditComponent); } - #escKeyDownHandler = (evt) => { - if (evt.key === 'Escape') { - evt.preventDefault(); - this.#pointEditComponent.reset(this.#point); - this.#replaceFormToPoint(); - document.removeEventListener('keydown', this.#escKeyDownHandler); - } - }; - #replacePointToForm() { replace(this.#pointEditComponent, this.#pointComponent); document.addEventListener('keydown', this.#escKeyDownHandler); @@ -96,16 +91,42 @@ export default class PointPresenter { this.#mode = Mode.DEFAULT; } + #escKeyDownHandler = (evt) => { + if (evt.key === 'Escape') { + evt.preventDefault(); + this.#pointEditComponent.reset(this.#point); + this.#replaceFormToPoint(); + document.removeEventListener('keydown', this.#escKeyDownHandler); + } + }; + #handleEditClick = () => { this.#replacePointToForm(); }; - #handleFormSubmit = (point) => { - this.#handleDataChange(point); + #handleFormSubmit = (update) => { + const isMinorUpdate = !isDatesEqual(this.#point.dateFrom, update.dateFrom) || !isDatesEqual(this.#point.dateTo, update.dateTo) || this.#point.basePrice !== update.basePrice; + this.#handleDataChange( + UserAction.UPDATE_POINT, + isMinorUpdate ? UpdateType.MINOR : UpdateType.PATCH, + update, + ); this.#replaceFormToPoint(); }; + #handleDeleteClick = (point) => { + this.#handleDataChange( + UserAction.DELETE_POINT, + UpdateType.MINOR, + point, + ); + }; + #handleFavoriteClick = () => { - this.#handleDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + this.#handleDataChange( + UserAction.UPDATE_POINT, + UpdateType.PATCH, + {...this.#point, isFavorite: !this.#point.isFavorite}, + ); }; } diff --git a/src/utils/common.js b/src/utils/common.js index 915c9a2..6c1799b 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -8,8 +8,4 @@ function capitalizeFirstLetter(text) { return textFirstCapitalLetter; } -function updateItem(items, update) { - return items.map((item) => item.id === update.id ? update : item); -} - -export {getRandomArrayElement, capitalizeFirstLetter, updateItem}; +export {getRandomArrayElement, capitalizeFirstLetter}; diff --git a/src/utils/points.js b/src/utils/points.js index f1c5ac8..6d84810 100644 --- a/src/utils/points.js +++ b/src/utils/points.js @@ -92,5 +92,9 @@ function toggleOffers(offers, id) { return [...offers, id]; } +function isDatesEqual(dateA, dateB) { + return (dateA === null && dateB === null) || dayjs(dateA).isSame(dateB, 'D'); +} + -export {humanizePointDate, humanizePointDuration, FORMATS, isCompletedPoints, isCurrentPoints, isPlannedPoints, sortPointDate, sortPointPrice, sortPointTime, toggleOffers}; +export {humanizePointDate, humanizePointDuration, FORMATS, isCompletedPoints, isCurrentPoints, isPlannedPoints, sortPointDate, sortPointPrice, sortPointTime, toggleOffers, isDatesEqual}; diff --git a/src/view/filters-view.js b/src/view/filters-view.js index f2bc8c1..02ffd5f 100644 --- a/src/view/filters-view.js +++ b/src/view/filters-view.js @@ -1,16 +1,17 @@ import AbstractView from '../framework/view/abstract-view.js'; -function createFilterItemTemplate({type}) { +function createFilterItemTemplate({type}, currentFilter) { + const isChecked = type === currentFilter ? 'checked' : ''; return `
- +
`; } -function createFiltersTemplate(filtersItem) { +function createFiltersTemplate(filtersItem, currentFilter) { return ( `
- ${filtersItem.map((e)=> createFilterItemTemplate(e)).join('')} + ${filtersItem.map((filter)=> createFilterItemTemplate(filter, currentFilter)).join('')}
` ); @@ -18,13 +19,27 @@ function createFiltersTemplate(filtersItem) { export default class FiltersView extends AbstractView{ #filters = null; + #currentFilter = null; + #handleFilterTypeChange = null; - constructor ({filters}) { + constructor ({filters, currentFilterType, onFilterTypeChange}) { super(); this.#filters = filters; + this.#currentFilter = currentFilterType; + this.#handleFilterTypeChange = onFilterTypeChange; + this.element.addEventListener('click', this.#filterTypeChangeHandler); } get template() { - return createFiltersTemplate(this.#filters); + return createFiltersTemplate(this.#filters, this.#currentFilter); } + + #filterTypeChangeHandler = (evt) => { + evt.preventDefault(); + if(evt.target.tagName === 'LABEL') { + const inputElement = evt.target.closest('.trip-filters__filter').querySelector('input'); + + this.#handleFilterTypeChange(inputElement.value); + } + }; } diff --git a/src/view/form-point-view.js b/src/view/form-point-view.js index f50fe1d..5b53ff0 100644 --- a/src/view/form-point-view.js +++ b/src/view/form-point-view.js @@ -1,7 +1,7 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view.js'; import { FORMATS, humanizePointDate, toggleOffers } from '../utils/points.js'; import { capitalizeFirstLetter } from '../utils/common.js'; - +import he from 'he'; import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; @@ -12,7 +12,7 @@ const BLANK_POINT = { dateFrom: '', dateTo: '', basePrice: '0', - offers: ['luggage', 'comfort', 'meal', 'seats', 'train'], + offers: [], }; function createTypeItemTemplate(type) { @@ -92,7 +92,7 @@ function createEventTemplate(point = BLANK_POINT, allOffers, allDestinations, ed - + ${allDestinations.map((e) => ``).join('')} @@ -100,10 +100,10 @@ function createEventTemplate(point = BLANK_POINT, allOffers, allDestinations, ed
- + - +
@@ -111,7 +111,7 @@ function createEventTemplate(point = BLANK_POINT, allOffers, allDestinations, ed basePrice € - +
@@ -127,16 +127,18 @@ function createEventTemplate(point = BLANK_POINT, allOffers, allDestinations, ed export default class FormPointView extends AbstractStatefulView{ #handleFormSubmit = null; + #handleDeleteClick = null; #startDatepicker = null; #endDatepicker = null; - constructor({point, allOffers, allDestinations, onFormSubmit}) { + constructor({point = BLANK_POINT, allOffers, allDestinations, onFormSubmit, onDeleteClick}) { super(); this._setState(FormPointView.parsePointToState(point)); this.allOffers = allOffers; this.allDestinations = allDestinations; this.isEdit = !!point.id; this.#handleFormSubmit = onFormSubmit; + this.#handleDeleteClick = onDeleteClick; this. _restoreHandlers(); } @@ -150,6 +152,11 @@ export default class FormPointView extends AbstractStatefulView{ this.#handleFormSubmit(FormPointView.parseStateToPoint(this._state)); }; + #formDeleteClickHandler = (evt) => { + evt.preventDefault(); + this.#handleDeleteClick(FormPointView.parseStateToPoint(this._state)); + }; + #pointOfferClickHandler = (evt) => { evt.preventDefault(); const labelElement = evt.target.closest('.event__offer-label'); @@ -170,11 +177,22 @@ export default class FormPointView extends AbstractStatefulView{ }); }; - #pointDestinationClickHandler = (evt) => { + #pointDestinationChangeHandler = (evt) => { + evt.preventDefault(); + const newDestination = this.allDestinations.find((destinations) => evt.target.value === destinations.name); + if(!newDestination) { + evt.target.value = ''; + return; + } + this.updateElement({ + destination: newDestination.id, + }); + }; + + #pointPriceChangeHandler = (evt) => { evt.preventDefault(); - const newDestination = this.allDestinations.find((destinations) => evt.target.value === destinations.name).id; this.updateElement({ - destination: newDestination, + basePrice: evt.target.value, }); }; @@ -227,7 +245,11 @@ export default class FormPointView extends AbstractStatefulView{ this.element.querySelector('.event__type-group') .addEventListener('click', this.#pointTypeClickHandler); this.element.querySelector('.event__input--destination') - .addEventListener('change', this.#pointDestinationClickHandler); + .addEventListener('change', this.#pointDestinationChangeHandler); + this.element.querySelector('.event__input--basePrice') + .addEventListener('change', this.#pointPriceChangeHandler); + this.element.querySelector('.event__reset-btn') + .addEventListener('click', this.#formDeleteClickHandler); this.#setStartDatepicker(); this.#setEndDatepicker(); diff --git a/src/view/new-point-button-view.js b/src/view/new-point-button-view.js new file mode 100644 index 0000000..af3bd14 --- /dev/null +++ b/src/view/new-point-button-view.js @@ -0,0 +1,24 @@ +import AbstractView from '../framework/view/abstract-view.js'; + +function createNewPointButtonTemplate() { + return ''; +} + +export default class NewPointButtonView extends AbstractView { + #handleClick = null; + + constructor({onClick}) { + super(); + this.#handleClick = onClick; + this.element.addEventListener('click', this.#clickHandler); + } + + get template() { + return createNewPointButtonTemplate(); + } + + #clickHandler = (evt) => { + evt.preventDefault(); + this.#handleClick(); + }; +} diff --git a/src/view/no-point-view.js b/src/view/no-point-view.js index 57ead32..6ce8101 100644 --- a/src/view/no-point-view.js +++ b/src/view/no-point-view.js @@ -1,20 +1,20 @@ import AbstractView from '../framework/view/abstract-view.js'; import { NO_POINT_MESSAGES } from '../const.js'; -function createNoPointTemplate(filterChecked) { +function createNoPointTemplate(filterType) { return ` -

${NO_POINT_MESSAGES[filterChecked.toUpperCase()]}

+

${NO_POINT_MESSAGES[filterType.toUpperCase()]}

`; } export default class NoPointView extends AbstractView { - #filterChecked = null; - constructor(filter) { + #filterType = null; + constructor({filterType}) { super(); - this.#filterChecked = filter; + this.#filterType = filterType; } get template() { - return createNoPointTemplate(this.#filterChecked); + return createNoPointTemplate(this.#filterType); } } diff --git a/src/view/sort-view.js b/src/view/sort-view.js index 2c3caad..8e69d83 100644 --- a/src/view/sort-view.js +++ b/src/view/sort-view.js @@ -23,10 +23,12 @@ function createSortTemplate(sortType) { export default class SortView extends AbstractView{ #handleSortTypeChange = null; #sortType = null; + constructor({onSortTypeChange, sortType}) { super(); this.#sortType = sortType; this. #handleSortTypeChange = onSortTypeChange; + this.element.addEventListener('click', this.#sortTypeChangeHandler); }