From c8cd966b4c5d12a8a4275b4685f7dd0fe2bc0af1 Mon Sep 17 00:00:00 2001 From: Darya Palitsyna Date: Sat, 25 May 2024 02:44:44 +0500 Subject: [PATCH 1/4] models, filters, presenters, no-point-view --- src/const.js | 16 ++- src/main.js | 25 +++- src/mock/filter.js | 12 -- src/model/filter-model.js | 15 +++ src/model/point-model.js | 43 ++++++- src/presenter/filter-presenter.js | 61 ++++++++++ src/presenter/point-presenter.js | 32 ++++- src/presenter/trip-presenter.js | 135 +++++++++++++++++----- src/template/event-list-empty-template.js | 18 ++- src/template/filter-template.js | 33 ++---- src/template/sort-template.js | 11 +- src/utils/common.js | 6 +- src/utils/points.js | 34 ++++++ src/utils/sort-points.js | 32 ----- src/view/editing-form-view.js | 15 ++- src/view/event-list-empty-view.js | 9 +- src/view/filter-points-view.js | 17 ++- src/view/sort-points-view.js | 6 +- 18 files changed, 394 insertions(+), 126 deletions(-) delete mode 100644 src/mock/filter.js create mode 100644 src/model/filter-model.js create mode 100644 src/presenter/filter-presenter.js create mode 100644 src/utils/points.js delete mode 100644 src/utils/sort-points.js diff --git a/src/const.js b/src/const.js index 7edc280..c3576e5 100644 --- a/src/const.js +++ b/src/const.js @@ -75,6 +75,18 @@ const SortType = [{ PRICE: 'price' }]; +const UserAction = { + UPDATE_POINT: 'UPDATE_POINT', + ADD_POINT: 'ADD_POINT', + DELETE_POINT: 'DELETE_POINT', +}; + +const UpdateType = { + PATCH: 'PATCH', + MINOR: 'MINOR', + MAJOR: 'MAJOR', +}; + export {POINT_EMPTY, POINT_TYPE, DESTINATION, @@ -85,4 +97,6 @@ export {POINT_EMPTY, POINTS_COUNT, FilterType, Mode, - SortType}; + SortType, + UserAction, + UpdateType}; diff --git a/src/main.js b/src/main.js index c8ff8fd..c6e615f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,14 +1,24 @@ import FilterPointsView from './view/filter-points-view.js'; import TripInfoView from './view/trip-info-view.js'; import TripPresenter from './presenter/trip-presenter.js'; +import FilterPresenter from './presenter/filter-presenter.js'; import { render, RenderPosition } from './framework/render.js'; import PointsModel from './model/point-model.js'; import DestinationModel from './model/destination-model.js'; import OffersModel from './model/offer-model.js'; +import FilterModel from './model/filter-model.js'; -import { generateFilters } from './mock/filter.js'; +//import { generateFilters } from './mock/filter.js'; + +const filters = [ + { + type: 'everything', + name: 'EVERYTHING', + count: 0, + } +] const filterHeaderElement = document.querySelector('.trip-controls'); const siteFilterElement = filterHeaderElement.querySelector('.trip-controls__filters'); @@ -18,17 +28,24 @@ const tripInfoElement = document.querySelector('.trip-main'); const pointsModel = new PointsModel(); const destinationModel = new DestinationModel(); const offersModel = new OffersModel(); +const filterModel = new FilterModel(); const tripPresenter = new TripPresenter({ listContainer: siteMainElement, pointsModel, destinationsModel: destinationModel, - offersModel + offersModel, + filterModel }); -const filters = generateFilters(pointsModel.points); +//const filters = generateFilters(pointsModel.points); +const filterPresenter = new FilterPresenter({ + filterContainer: siteMainElement, + filterModel, + pointsModel +}); -render(new FilterPointsView({ filters }), siteFilterElement); render(new TripInfoView(), tripInfoElement, RenderPosition.AFTERBEGIN); +filterPresenter.init(); tripPresenter.init(); diff --git a/src/mock/filter.js b/src/mock/filter.js deleted file mode 100644 index 45a6c57..0000000 --- a/src/mock/filter.js +++ /dev/null @@ -1,12 +0,0 @@ -import { filter } from '../utils/filter'; - -function generateFilters(points) { - return Object.entries(filter).map( - ([filterType, filterPoints]) => ({ - type: filterType, - count: filterPoints(points).length, - }), - ); -} - -export {generateFilters}; diff --git a/src/model/filter-model.js b/src/model/filter-model.js new file mode 100644 index 0000000..02dfce0 --- /dev/null +++ b/src/model/filter-model.js @@ -0,0 +1,15 @@ +import Observable from '../framework/observable.js'; +import {FilterType} from '../const.js'; + +export default class FilterModel extends Observable { + #filter = FilterType.EVERYTHING; + + get filter() { + return this.#filter; + } + + setFilter(updateType, filter) { + this.#filter = filter; + this._notify(updateType, filter); + } +} diff --git a/src/model/point-model.js b/src/model/point-model.js index 1285714..d82bd23 100644 --- a/src/model/point-model.js +++ b/src/model/point-model.js @@ -1,10 +1,51 @@ +import Observable from '../framework/observable'; import { getRandomPoint } from '../mock/point'; import { POINTS_COUNT } from '../const'; -export default class PointsModel { +export default class PointsModel extends Observable { #points = Array.from({length: POINTS_COUNT }, () => getRandomPoint()); get points() { return this.#points; } + + 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/filter-presenter.js b/src/presenter/filter-presenter.js new file mode 100644 index 0000000..60f5a43 --- /dev/null +++ b/src/presenter/filter-presenter.js @@ -0,0 +1,61 @@ +import {render, replace, remove} from '../framework/render.js'; +import FilterPointsView from '../view/filter-points-view.js'; +import {filter} from '../utils/filter.js'; +import {FilterType, 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(FilterType).map((type) => ({ + type, + count: filter[type](points).length + })); + } + + init() { + const filters = this.filters; + const prevFilterComponent = this.#filterComponent; + + this.#filterComponent = new FilterPointsView({ + 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/point-presenter.js b/src/presenter/point-presenter.js index 0fc8be5..f3fbaf7 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -4,6 +4,7 @@ import ListPointsView from '../view/list-points-view'; import EditPointView from '../view/editing-form-view'; import { Mode } from '../const'; +import { UserAction, UpdateType } from '../const'; export default class PointPresenter { #pointListContainer = null; @@ -56,7 +57,8 @@ export default class PointPresenter { pointDestinations: this.#destinationsModel.destinations, pointOffers: this.#offersModel.allOffers, onResetClick: this.#resetButtonClickHandler, - onSubmitClick: this.#handleFormSubmit + onSubmitClick: this.#handleFormSubmit, + onDeleteClick: this.#pointDeleteClickHandler }); if (!prevPointComponent || !prevPointEditComponent) { @@ -109,8 +111,18 @@ export default class PointPresenter { this.#mode = Mode.DEFAULT; } - #handleFormSubmit = (point) => { - this.#handleDataChange(point); + #handleFormSubmit = (update) => { + const isMinorUpdate = + !isDatesEqual(this.#point.dateFrom, update.dateFrom) || + !isDatesEqual(this.#point.dateTo, update.dateTo) || + !isPriceEqual(this.#point.basePrice, update.basePrice); + + this.#handleDataChange( + UserAction.UPDATE_POINT, + isMinorUpdate ? UpdateType.MINOR : UpdateType.PATCH, + update, + ); + this.#replaceEditToPoint(); }; @@ -122,7 +134,19 @@ export default class PointPresenter { this.#replaceEditToPoint(); }; + #pointDeleteClickHandler = (point) => { + this.#handleDataChange( + UserAction.DELETE_POINT, + UpdateType.MINOR, + point + ) + }; + #pointFavoriteHandler = () => { - this.#handleDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + this.#handleDataChange( + UserAction.UPDATE_POINT, + UpdateType.MINOR, + {...this.#point, isFavorite: !this.#point.isFavorite} + ); }; } diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index f83ac9d..f2ceb35 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -2,44 +2,72 @@ import ListView from '../view/list-view.js'; import EventListEmptyView from '../view/event-list-empty-view.js'; import SortPointsView from '../view/sort-points-view.js'; -import { render, RenderPosition } from '../framework/render.js'; +import { remove, render, RenderPosition } from '../framework/render.js'; import PointPresenter from './point-presenter.js'; -import { updateItem } from '../utils/common.js'; -import { sortPointsByTime, sortPointsByPrice } from '../utils/sort-points.js'; -import { SortType } from '../const.js'; +import { sortPointsByTime, sortPointsByPrice } from '../utils/points.js'; +import { filter } from '../utils/filter.js'; +import { SortType, UserAction, UpdateType, FilterType } from '../const.js'; export default class TripPresenter { #listComponent = new ListView(); #sortComponent = null; + #noPointsComponent = null; - #listContainer; - #pointsModel; - #destinationsModel; - #offersModel; + #listContainer = null; + #pointsModel = null; + #destinationsModel = null; + #offersModel = null; + #filterModel = null; - #tripPoint = []; + //#tripPoint = []; #currentSortType = SortType.DAY; - #sourcedTripPoints = []; + #filterType = FilterType.EVERYTHING; + // #sourcedTripPoints = []; #pointPresenters = new Map(); - constructor({ listContainer, pointsModel, destinationsModel, offersModel }) { + constructor({ listContainer, pointsModel, destinationsModel, offersModel, filterModel }) { this.#listContainer = listContainer; this.#pointsModel = pointsModel; this.#destinationsModel = destinationsModel; this.#offersModel = offersModel; + this.#filterModel = filterModel; - this.#tripPoint = [...this.#pointsModel.points]; + this.#pointsModel.addObserver(this.#handleModelEvent); + this.#filterModel.addObserver(this.#handleModelEvent); + //this.#tripPoint = [...this.#pointsModel.points]; + } + + get points() { + this.#filterType = this.#filterModel.filter; + const points = this.#pointsModel.tasks; + const filteredPoints = filter[this.#filterType](points); + + switch(this.#currentSortType){ + case SortType.TIME: + filteredPoints.sort(sortPointsByTime); + case SortType.PRICE: + filteredPoints.sort(sortPointsByPrice); + } + + return filteredPoints; } init() { - this.#sourcedTripPoints = [...this.#pointsModel.points]; + //this.#sourcedTripPoints = [...this.#pointsModel.points]; this.#renderBoard(); } + #renderNoPoints = () => { + this.#noPointsComponent = new EventListEmptyView({ + filterType: this.#filterType, + }) + render(this.#noPointsComponent, this.#listComponent.element, RenderPosition.AFTERBEGIN); + }; + #renderPoint(point) { if (!this.#listComponent.element) { throw new Error('List component element is not defined'); @@ -47,7 +75,7 @@ export default class TripPresenter { const pointPresenter = new PointPresenter({ pointListContainer: this.#listComponent.element, - onDataChange: this.#handlePointChange, + onDataChange: this.#handleViewAction, onModeChange: this.#handleModeChange, destinationsModel: this.#destinationsModel, offersModel: this.#offersModel @@ -57,10 +85,8 @@ export default class TripPresenter { this.#pointPresenters.set(point.id, pointPresenter); } - #renderPoints = () => { - this.#tripPoint.forEach((point) => { - this.#renderPoint(point); - }); + #renderPoints = (points) => { + points.forEach((point) => {this.#renderPoint(point)}); }; #renderPointContainer = () => { @@ -69,6 +95,7 @@ export default class TripPresenter { #renderSort() { this.#sortComponent = new SortPointsView({ + currentSortType: this.#currentSortType, onSortTypeChange: this.#handleSortTypeChange }); @@ -77,31 +104,79 @@ export default class TripPresenter { #renderBoard = () => { /* console.log('Rendering board'); */ - if (this.#tripPoint.length === 0) { - render(new EventListEmptyView(), this.#listContainer); + const points = this.points; + const pointsCount = points.length; + + if (pointsCount === 0) { + this.#renderNoPoints(); return; } this.#renderSort(); this.#renderPointContainer(); - this.#renderPoints(); + this.#renderPoints(this.points); }; - #clearPointsList() { + /* #clearPointsList() { this.#pointPresenters.forEach((presenter) => presenter.destroy()); this.#pointPresenters.clear(); - } + } */ #handleModeChange = () => { this.#pointPresenters.forEach((presenter) => presenter.resetView()); }; - #handlePointChange = (updatePoint) => { - this.#tripPoint = updateItem(this.#tripPoint, updatePoint); + /* #handlePointChange = (updatePoint) => { + //this.#tripPoint = updateItem(this.#tripPoint, updatePoint); this.#pointPresenters.get(updatePoint.id).init(updatePoint); + }; */ + + #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; + } }; - #sortPoints(sortType) { + #clearBoard = ({resetSortType = false} = {}) => { + this.#pointPresenters.forEach((presenter) => presenter.destroy()); + this.#pointPresenters.clear(); + + remove(this.#sortComponent); + + if (this.#noPointsComponent) { + remove(this.#noPointsComponent); + } + + if(resetSortType) { + this.#currentSortType = SortType.DAY; + } + }; + + #handleModelEvent = (updateType, data) => { + switch (updateType) { + case UpdateType.PATCH: + this.#pointPresenters.get(data.id).init(data); + break; + case UpdateType.MINOR: + this.#clearBoard(); + this.#renderBoard(); + break; + case UpdateType.MAJOR: + this.#clearBoard({resetSortType: true}); + this.#renderBoard(); + break; + } + }; + + /* #sortPoints(sortType) { switch(sortType) { case SortType.TIME: this.#tripPoint.sort(sortPointsByTime); @@ -114,15 +189,15 @@ export default class TripPresenter { } this.#currentSortType = sortType; - } + } */ #handleSortTypeChange = (sortType) => { if (this.#currentSortType === sortType) { return; } - this.#sortPoints(sortType); - this.#clearPointsList(); - this.#renderPoints(); + this.#currentSortType = sortType; + this.#clearBoard(); + this.#renderBoard(); }; } diff --git a/src/template/event-list-empty-template.js b/src/template/event-list-empty-template.js index 1510923..97bc8c0 100644 --- a/src/template/event-list-empty-template.js +++ b/src/template/event-list-empty-template.js @@ -1,5 +1,17 @@ -function createEmptyListPointsTemplate() { - return '

Click New Event to create your first point

'; -} +import {FilterType} from '../const.js'; + +const NoPointsTextType = { + [FilterType.EVERYTHING]: 'Click New Event to create your first point', + [FilterType.PAST]: 'There are no past events now', + [FilterType.FUTURE]: 'There are no future events now', +}; + +const createEmptyListPointsTemplate = (filterType) => { + const noPointTextValue = NoPointsTextType[filterType]; + + return ( + `

${noPointTextValue}

` + ); +}; export {createEmptyListPointsTemplate}; diff --git a/src/template/filter-template.js b/src/template/filter-template.js index 360e486..e88e95b 100644 --- a/src/template/filter-template.js +++ b/src/template/filter-template.js @@ -1,27 +1,16 @@ -function createFilterTemplate(){ - return `
-
- - -
- -
- - -
- -
- - -
- -
- - -
+function createFilterItems(filters, currentFilterType) { + return filters.map((type) => `
+ + +
`).join(''); +} +function createFilterTemplate(filters, currentFilterType){ + return ` + ${createFilterItems(filters, currentFilterType)}
`; } -export {createFilterTemplate}; +export {createFilterTemplate, createFilterItems}; diff --git a/src/template/sort-template.js b/src/template/sort-template.js index 0ae2840..9ff7cb9 100644 --- a/src/template/sort-template.js +++ b/src/template/sort-template.js @@ -1,9 +1,10 @@ import { SortType } from '../const'; -function createSortTemplate() { +function createSortTemplate(currentSortType) { return `
- +
@@ -13,12 +14,14 @@ function createSortTemplate() {
- +
- +
diff --git a/src/utils/common.js b/src/utils/common.js index 863ae5e..3a82e57 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -6,8 +6,4 @@ function getRandomValue(lower = 0, upper = 1000) { return Math.round((upper - lower) * Math.random() + lower); } -function updateItem(items, update) { - return items.map((item) => item.id === update.id ? update : item); -} - -export {getRandomArrayElement, getRandomValue, updateItem}; +export {getRandomArrayElement, getRandomValue}; diff --git a/src/utils/points.js b/src/utils/points.js new file mode 100644 index 0000000..92f660d --- /dev/null +++ b/src/utils/points.js @@ -0,0 +1,34 @@ +import dayjs from 'dayjs'; + +function sortPointsByDay(pointA, pointB) { + return dayjs(pointA.dateFrom).diff(dayjs(pointB.dateFrom)); +} + +function sortPointsByTime(pointA, pointB) { + const durationA = dayjs(pointA.dateTo).diff(dayjs(pointA.dateFrom)); + const durationB = dayjs(pointB.dateTo).diff(dayjs(pointB.dateFrom)); + + return durationA - durationB; +} + +function sortPointsByPrice(pointA, pointB){ + const diff = pointA.basePrice - pointB.basePrice; + + if (diff > 0) { + return -1; + } else if (diff < 0) { + return 1; + } else { + return 0; + } +} + +function isDatesEqual(dateA, dateB) { + return (dateA === null && dateB === null) || dayjs(dateA).isSame(dateB, 'D'); +} + +function isPriceEqual(priceA, priceB) { + return priceA === priceB; +} + +export {sortPointsByPrice, sortPointsByTime, sortPointsByDay, isDatesEqual, isPriceEqual}; diff --git a/src/utils/sort-points.js b/src/utils/sort-points.js deleted file mode 100644 index 5eb6bad..0000000 --- a/src/utils/sort-points.js +++ /dev/null @@ -1,32 +0,0 @@ -import dayjs from 'dayjs'; - -function sortPointsByDay(pointA, pointB) { - return dayjs(pointA.dateFrom).diff(dayjs(pointB.dateFrom)); -} - -function sortPointsByTime(pointA, pointB) { - const durationA = dayjs.duration(dayjs(pointA.dateTo).diff(dayjs(pointA.dateFrom), 'minute')); - const durationB = dayjs.duration(dayjs(pointB.dateTo).diff(dayjs(pointB.dateFrom), 'minute')); - - if (durationA.asMilliseconds() > durationB.asMilliseconds()) { - return -1; - } else if (durationA.asMilliseconds() < durationB.asMilliseconds()) { - return 1; - } else { - return 0; - } -} - -function sortPointsByPrice(pointA, pointB){ - const diff = pointA.basePrice - pointB.basePrice; - - if (diff > 0) { - return -1; - } else if (diff < 0) { - return 1; - } else { - return 0; - } -} - -export {sortPointsByPrice, sortPointsByTime, sortPointsByDay}; diff --git a/src/view/editing-form-view.js b/src/view/editing-form-view.js index 3885a4a..488fe2e 100644 --- a/src/view/editing-form-view.js +++ b/src/view/editing-form-view.js @@ -10,15 +10,17 @@ export default class EditPointView extends AbstractStatefulView { #pointOffers = null; #handleResetClick = null; #handleSubmitClick = null; + #handlePointDelete = null; #datepickerFrom = null; #datepickerTo = null; - constructor({point = POINT_EMPTY, pointDestinations, pointOffers, onResetClick, onSubmitClick}) { + constructor({point = POINT_EMPTY, pointDestinations, pointOffers, onResetClick, onSubmitClick, onDeleteClick}) { super(); this.#pointDestinations = pointDestinations; this.#pointOffers = pointOffers; this.#handleResetClick = onResetClick; this.#handleSubmitClick = onSubmitClick; + this.#handlePointDelete = onDeleteClick; this._setState(EditPointView.parsePointToState({point})); @@ -60,7 +62,7 @@ export default class EditPointView extends AbstractStatefulView { this.element .querySelector('.event__rollup-btn') - .addEventListener('click', this.#resetButtonClickHandler); + ?.addEventListener('click', this.#resetButtonClickHandler); this.element .querySelector('.event__type-group') @@ -78,6 +80,10 @@ export default class EditPointView extends AbstractStatefulView { .querySelector('.event__input--destination') .addEventListener('change', this.#destinationChangeHandler); + this.element + .querySelector('.event__reset-btn') + .addEventListener('click', this.#deleteClickHandler); + this.#setDatepickers(); }; @@ -91,6 +97,11 @@ export default class EditPointView extends AbstractStatefulView { this.#handleResetClick(EditPointView.parseStateToPoint(this._state)); }; + #deleteClickHandler = (evt) => { + evt.preventDefault(); + this.#handlePointDelete(EditPointView.parseStateToPoint(this._state)); + }; + #typeChangeHandler = (evt) => { this.updateElement( { point: { diff --git a/src/view/event-list-empty-view.js b/src/view/event-list-empty-view.js index 2af470a..a08fa97 100644 --- a/src/view/event-list-empty-view.js +++ b/src/view/event-list-empty-view.js @@ -2,7 +2,14 @@ import AbstractView from '../framework/view/abstract-view'; import { createEmptyListPointsTemplate } from '../template/event-list-empty-template'; export default class EventListEmptyView extends AbstractView { + #filterType = null; + + constructor({filterType}) { + super(); + this.#filterType = filterType; + } + get template() { - return createEmptyListPointsTemplate(); + return createEmptyListPointsTemplate(this.#filterType); } } diff --git a/src/view/filter-points-view.js b/src/view/filter-points-view.js index eae65b0..c2d798b 100644 --- a/src/view/filter-points-view.js +++ b/src/view/filter-points-view.js @@ -2,14 +2,25 @@ import AbstractView from '../framework/view/abstract-view.js'; import { createFilterTemplate } from '../template/filter-template.js'; export default class FilterPointsView extends AbstractView { - #filters = []; + #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('change', this.#filterTypeChangeHandler); } get template() { - return createFilterTemplate(); + return createFilterTemplate(this.#filters, this.#currentFilter); + } + + #filterTypeChangeHandler = (evt) => { + evt.preventDefault(); + this.#handleFilterTypeChange(evt.target.value); } } diff --git a/src/view/sort-points-view.js b/src/view/sort-points-view.js index 47f308f..d15d181 100644 --- a/src/view/sort-points-view.js +++ b/src/view/sort-points-view.js @@ -2,17 +2,19 @@ import AbstractView from '../framework/view/abstract-view.js'; import { createSortTemplate } from '../template/sort-template.js'; export default class SortPointsView extends AbstractView { + #currentSortType = null; #handleSortTypeChange = null; - constructor({onSortTypeChange}) { + constructor({currentSortType, onSortTypeChange}) { super(); + this.#currentSortType = currentSortType; this.#handleSortTypeChange = onSortTypeChange; this.element.addEventListener('click', this.#sortTypeChangeHandler); } get template() { - return createSortTemplate(); + return createSortTemplate(this.#currentSortType); } #sortTypeChangeHandler = (evt) => { From b9d3430d984a3e2491def8aea6bb28ded88a5ca1 Mon Sep 17 00:00:00 2001 From: Darya Palitsyna Date: Sun, 26 May 2024 14:28:33 +0500 Subject: [PATCH 2/4] =?UTF-8?q?7.10.=20=D0=9C=D0=B5=D0=BD=D1=8F=D0=B9-?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D1=8F=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 5 +- package.json | 1 + src/const.js | 4 +- src/main.js | 28 +++++++--- src/presenter/filter-presenter.js | 3 +- src/presenter/new-point-presenter.js | 66 +++++++++++++++++++++++ src/presenter/trip-presenter.js | 21 +++++++- src/template/editing-form-template.js | 5 +- src/template/filter-template.js | 2 +- src/template/new-point-button-template.js | 5 ++ src/view/new-point-button-view.js | 21 ++++++++ 11 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 src/presenter/new-point-presenter.js create mode 100644 src/template/new-point-button-template.js create mode 100644 src/view/new-point-button-view.js diff --git a/package-lock.json b/package-lock.json index d87ff4a..26a7862 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "dayjs": "1.11.10", "flatpickr": "^4.6.13", + "he": "^1.2.0", "nanoid": "^5.0.7" }, "devDependencies": { @@ -4495,7 +4496,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" } @@ -11109,8 +11109,7 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "hpack.js": { "version": "2.1.6", diff --git a/package.json b/package.json index c0d9ea1..62ef83d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "dayjs": "1.11.10", "flatpickr": "^4.6.13", + "he": "^1.2.0", "nanoid": "^5.0.7" } } diff --git a/src/const.js b/src/const.js index c3576e5..b916eac 100644 --- a/src/const.js +++ b/src/const.js @@ -57,12 +57,12 @@ const OFFERS = [ 'Travel by train', ]; -const FilterType = [{ +const FilterType = { EVERYTHING: 'everything', FUTURE: 'future', PRESENT: 'present', PAST: 'past' -}]; +}; const Mode = { DEFAULT: 'DEFAULT', diff --git a/src/main.js b/src/main.js index c6e615f..e2abd4c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,8 @@ -import FilterPointsView from './view/filter-points-view.js'; +//import FilterPointsView from './view/filter-points-view.js'; import TripInfoView from './view/trip-info-view.js'; import TripPresenter from './presenter/trip-presenter.js'; import FilterPresenter from './presenter/filter-presenter.js'; +import NewPointButtonView from './view/new-point-button-view.js'; import { render, RenderPosition } from './framework/render.js'; @@ -12,16 +13,16 @@ import FilterModel from './model/filter-model.js'; //import { generateFilters } from './mock/filter.js'; -const filters = [ +/* const filters = [ { type: 'everything', name: 'EVERYTHING', count: 0, } -] +] */ -const filterHeaderElement = document.querySelector('.trip-controls'); -const siteFilterElement = filterHeaderElement.querySelector('.trip-controls__filters'); +//const filterHeaderElement = document.querySelector('.trip-controls'); +//const siteFilterElement = filterHeaderElement.querySelector('.trip-controls__filters'); const siteMainElement = document.querySelector('.page-main'); const tripInfoElement = document.querySelector('.trip-main'); @@ -35,7 +36,8 @@ const tripPresenter = new TripPresenter({ pointsModel, destinationsModel: destinationModel, offersModel, - filterModel + filterModel, + onNewPointDestroy: handleNewPointFormClose }); //const filters = generateFilters(pointsModel.points); @@ -45,7 +47,21 @@ const filterPresenter = new FilterPresenter({ pointsModel }); +const newPointButtonComponent = new NewPointButtonView({ + onClick: handleNewPointButtonClick +}); + +function handleNewPointFormClose() { + newPointButtonComponent.element.disabled = false; +} + +function handleNewPointButtonClick() { + tripPresenter.createPoint(); + newPointButtonComponent.element.disabled = true; +} + render(new TripInfoView(), tripInfoElement, RenderPosition.AFTERBEGIN); +render(newPointButtonComponent, tripInfoElement); filterPresenter.init(); tripPresenter.init(); diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js index 60f5a43..c441545 100644 --- a/src/presenter/filter-presenter.js +++ b/src/presenter/filter-presenter.js @@ -23,8 +23,7 @@ export default class FilterPresenter { const points = this.#pointsModel.points; return Object.values(FilterType).map((type) => ({ - type, - count: filter[type](points).length + type })); } diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js new file mode 100644 index 0000000..f3dfd89 --- /dev/null +++ b/src/presenter/new-point-presenter.js @@ -0,0 +1,66 @@ +import {remove, render, RenderPosition} from '../framework/render.js'; +import EditPointView from '../view/editing-form-view.js'; +import {nanoid} from 'nanoid'; +import {UserAction, UpdateType} from '../const.js'; + +export default class NewPointPresenter { + #pointListContainer = null; + #handleDataChange = null; + #handleDestroy = null; + + #pointEditComponent = null; + + constructor({pointListContainer, onDataChange, onDestroy}) { + this.#pointListContainer = pointListContainer; + this.#handleDataChange = onDataChange; + this.#handleDestroy = onDestroy; + } + + init() { + if (this.#pointEditComponent !== null) { + return; + } + + this.#pointEditComponent = new EditPointView({ + 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 = (point) => { + this.#handleDataChange( + UserAction.ADD_POINT, + UpdateType.MINOR, + {id: nanoid(), ...point}, + ); + this.destroy(); + }; + + #handleDeleteClick = () => { + this.destroy(); + }; + + #escKeyDownHandler = (evt) => { + if (evt.key === 'Escape' || evt.key === 'Esc') { + evt.preventDefault(); + this.destroy(); + } + }; +} diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index f2ceb35..9450ece 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -5,10 +5,12 @@ import SortPointsView from '../view/sort-points-view.js'; import { remove, render, RenderPosition } from '../framework/render.js'; import PointPresenter from './point-presenter.js'; +import NewPointPresenter from './new-point-presenter.js'; import { sortPointsByTime, sortPointsByPrice } from '../utils/points.js'; import { filter } from '../utils/filter.js'; import { SortType, UserAction, UpdateType, FilterType } from '../const.js'; +import EditPointView from '../view/editing-form-view.js'; export default class TripPresenter { #listComponent = new ListView(); @@ -27,14 +29,21 @@ export default class TripPresenter { // #sourcedTripPoints = []; #pointPresenters = new Map(); + #newPointPresenter = null; - constructor({ listContainer, pointsModel, destinationsModel, offersModel, filterModel }) { + constructor({ listContainer, pointsModel, destinationsModel, offersModel, filterModel, onNewPointDestroy }) { this.#listContainer = listContainer; this.#pointsModel = pointsModel; this.#destinationsModel = destinationsModel; this.#offersModel = offersModel; this.#filterModel = filterModel; + this.#newPointPresenter = new EditPointView({ + pointListContainer: this.#listComponent.element, + onDataChange: this.#handleViewAction, + onDestroy: onNewPointDestroy + }); + this.#pointsModel.addObserver(this.#handleModelEvent); this.#filterModel.addObserver(this.#handleModelEvent); //this.#tripPoint = [...this.#pointsModel.points]; @@ -42,7 +51,7 @@ export default class TripPresenter { get points() { this.#filterType = this.#filterModel.filter; - const points = this.#pointsModel.tasks; + const points = this.#pointsModel.points; const filteredPoints = filter[this.#filterType](points); switch(this.#currentSortType){ @@ -61,6 +70,12 @@ export default class TripPresenter { this.#renderBoard(); } + createPoint = () => { + this.#currentSortType = SortType.DAY; + this.#filterModel.setFilter(UpdateType.MAJOR, FilterType.EVERYTHING); + this.#newPointPresenter.init(); + }; + #renderNoPoints = () => { this.#noPointsComponent = new EventListEmptyView({ filterType: this.#filterType, @@ -123,6 +138,7 @@ export default class TripPresenter { } */ #handleModeChange = () => { + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.resetView()); }; @@ -146,6 +162,7 @@ export default class TripPresenter { }; #clearBoard = ({resetSortType = false} = {}) => { + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.destroy()); this.#pointPresenters.clear(); diff --git a/src/template/editing-form-template.js b/src/template/editing-form-template.js index b65d676..85f8217 100644 --- a/src/template/editing-form-template.js +++ b/src/template/editing-form-template.js @@ -1,6 +1,7 @@ import { POINT_TYPE, OFFERS } from '../const'; import { formatFullDate } from '../utils/day'; import { getRandomValue } from '../utils/common'; +import he from 'he'; function createPointType() { return POINT_TYPE.map((type) => `
@@ -21,6 +22,7 @@ function createPointOffer() { } function createEditPointTemplate({point}) { + return `
  • @@ -64,7 +66,8 @@ function createEditPointTemplate({point}) { Price € - +
  • diff --git a/src/template/filter-template.js b/src/template/filter-template.js index e88e95b..adb11d5 100644 --- a/src/template/filter-template.js +++ b/src/template/filter-template.js @@ -1,5 +1,5 @@ function createFilterItems(filters, currentFilterType) { - return filters.map((type) => `
    + return filters.filters.map((type) => `
    diff --git a/src/template/new-point-button-template.js b/src/template/new-point-button-template.js new file mode 100644 index 0000000..51c75b4 --- /dev/null +++ b/src/template/new-point-button-template.js @@ -0,0 +1,5 @@ +function createNewPointButtonTemplate() { + return '' +} + +export {createNewPointButtonTemplate}; diff --git a/src/view/new-point-button-view.js b/src/view/new-point-button-view.js new file mode 100644 index 0000000..ca081ab --- /dev/null +++ b/src/view/new-point-button-view.js @@ -0,0 +1,21 @@ +import { createNewPointButtonTemplate } from '../template/new-point-button-template'; +import AbstractView from '../framework/view/abstract-view'; + +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(); + }; +} From a1579076c8c44f191d7fd150f8f316511f409547 Mon Sep 17 00:00:00 2001 From: Darya Palitsyna Date: Sun, 26 May 2024 14:38:34 +0500 Subject: [PATCH 3/4] =?UTF-8?q?7.10.=20=D0=9C=D0=B5=D0=BD=D1=8F=D0=B9-?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D1=8F=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/filter-presenter.js | 4 +--- src/presenter/point-presenter.js | 3 ++- src/presenter/trip-presenter.js | 9 +++++---- src/template/editing-form-template.js | 2 +- src/view/filter-points-view.js | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js index c441545..c73aa11 100644 --- a/src/presenter/filter-presenter.js +++ b/src/presenter/filter-presenter.js @@ -1,6 +1,6 @@ import {render, replace, remove} from '../framework/render.js'; import FilterPointsView from '../view/filter-points-view.js'; -import {filter} from '../utils/filter.js'; +//import {filter} from '../utils/filter.js'; import {FilterType, UpdateType} from '../const.js'; export default class FilterPresenter { @@ -20,8 +20,6 @@ export default class FilterPresenter { } get filters() { - const points = this.#pointsModel.points; - return Object.values(FilterType).map((type) => ({ type })); diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index f3fbaf7..359445a 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -5,6 +5,7 @@ import EditPointView from '../view/editing-form-view'; import { Mode } from '../const'; import { UserAction, UpdateType } from '../const'; +import { isDatesEqual, isPriceEqual } from '../utils/points'; export default class PointPresenter { #pointListContainer = null; @@ -139,7 +140,7 @@ export default class PointPresenter { UserAction.DELETE_POINT, UpdateType.MINOR, point - ) + ); }; #pointFavoriteHandler = () => { diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index 9450ece..6dbecaa 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -26,7 +26,7 @@ export default class TripPresenter { //#tripPoint = []; #currentSortType = SortType.DAY; #filterType = FilterType.EVERYTHING; - // #sourcedTripPoints = []; + //#sourcedTripPoints = []; #pointPresenters = new Map(); #newPointPresenter = null; @@ -56,7 +56,8 @@ export default class TripPresenter { switch(this.#currentSortType){ case SortType.TIME: - filteredPoints.sort(sortPointsByTime); + filteredPoints.sort(sortPointsByTime); + break; case SortType.PRICE: filteredPoints.sort(sortPointsByPrice); } @@ -79,7 +80,7 @@ export default class TripPresenter { #renderNoPoints = () => { this.#noPointsComponent = new EventListEmptyView({ filterType: this.#filterType, - }) + }); render(this.#noPointsComponent, this.#listComponent.element, RenderPosition.AFTERBEGIN); }; @@ -101,7 +102,7 @@ export default class TripPresenter { } #renderPoints = (points) => { - points.forEach((point) => {this.#renderPoint(point)}); + points.forEach((point) => {this.#renderPoint(point);}); }; #renderPointContainer = () => { diff --git a/src/template/editing-form-template.js b/src/template/editing-form-template.js index 85f8217..b1d6bf6 100644 --- a/src/template/editing-form-template.js +++ b/src/template/editing-form-template.js @@ -1,7 +1,7 @@ import { POINT_TYPE, OFFERS } from '../const'; import { formatFullDate } from '../utils/day'; import { getRandomValue } from '../utils/common'; -import he from 'he'; +//import he from 'he'; function createPointType() { return POINT_TYPE.map((type) => `
    diff --git a/src/view/filter-points-view.js b/src/view/filter-points-view.js index c2d798b..a10f482 100644 --- a/src/view/filter-points-view.js +++ b/src/view/filter-points-view.js @@ -22,5 +22,5 @@ export default class FilterPointsView extends AbstractView { #filterTypeChangeHandler = (evt) => { evt.preventDefault(); this.#handleFilterTypeChange(evt.target.value); - } + }; } From 8ea865914ce002a503353111e9b09b25d1d72cbd Mon Sep 17 00:00:00 2001 From: Darya Palitsyna Date: Sun, 26 May 2024 14:40:31 +0500 Subject: [PATCH 4/4] =?UTF-8?q?7.10.=20=D0=9C=D0=B5=D0=BD=D1=8F=D0=B9-?= =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D1=8F=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/trip-presenter.js | 2 +- src/template/new-point-button-template.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index 6dbecaa..a07a67b 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -5,7 +5,7 @@ import SortPointsView from '../view/sort-points-view.js'; import { remove, render, RenderPosition } from '../framework/render.js'; import PointPresenter from './point-presenter.js'; -import NewPointPresenter from './new-point-presenter.js'; +//import NewPointPresenter from './new-point-presenter.js'; import { sortPointsByTime, sortPointsByPrice } from '../utils/points.js'; import { filter } from '../utils/filter.js'; diff --git a/src/template/new-point-button-template.js b/src/template/new-point-button-template.js index 51c75b4..66fb230 100644 --- a/src/template/new-point-button-template.js +++ b/src/template/new-point-button-template.js @@ -1,5 +1,5 @@ function createNewPointButtonTemplate() { - return '' + return ''; } export {createNewPointButtonTemplate};