From fd74fd85c9d6782d46d80454f2c442f2ef862cb3 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Wed, 2 Oct 2024 20:41:12 +0300 Subject: [PATCH 01/37] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/const.js | 18 ++++- src/main.js | 8 ++- src/mocs/filters.js | 9 --- src/model/filters-model.js | 15 +++++ src/model/point-model.js | 42 +++++++++++- src/presenter/filter-presenter.js | 56 ++++++++++++++-- src/presenter/point-presenter.js | 15 ++++- src/presenter/presenter.js | 105 +++++++++++++++++++----------- src/utils/common.js | 3 +- src/view/filters.js | 20 ++++-- 10 files changed, 222 insertions(+), 69 deletions(-) delete mode 100644 src/mocs/filters.js create mode 100644 src/model/filters-model.js diff --git a/src/const.js b/src/const.js index 1b0c943..d158e35 100644 --- a/src/const.js +++ b/src/const.js @@ -190,8 +190,8 @@ const POINTS = [ destination: DESTINATION_CITIES[0], event: EVENT_TYPES[0], offer: OFFER_TYPES[0], - dateFrom: '2024-07-10T21:39', - dateTo: '2024-07-18T21:44', + dateFrom: '2024-11-10T21:39', + dateTo: '2024-11-18T21:44', }, { isFavorite: false, @@ -240,4 +240,16 @@ const SortType = { PRICE: 'price', }; -export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType}; +const UserAction = { + UPDATE_POINT: 'UPDATE_POINT', + ADD_POINT: 'ADD_TASK', + DELETE_POINT: 'DELETE_POINT', +}; + +const UpdateType = { + PATCH: 'PATCH', + MINOR: 'MINOR', + MAJOR: 'MAJOR', +}; + +export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType, UserAction, UpdateType}; diff --git a/src/main.js b/src/main.js index dba1657..7bb2f22 100644 --- a/src/main.js +++ b/src/main.js @@ -1,19 +1,25 @@ import Presenter from './presenter/presenter.js'; import FilterPresenter from './presenter/filter-presenter.js'; import PointModel from './model/point-model.js'; +import FiltersModel from './model/filters-model.js'; const siteBody = document.querySelector('.page-body'); const siteFilters = siteBody.querySelector('.trip-controls__filters'); const siteContainer = siteBody.querySelector('.trip-events'); + const pointModels = new PointModel(); +const filtersModel = new FiltersModel(); + const pagePresenter = new Presenter({ mainContainer: siteContainer, pointModels, + filtersModel, }); const filterPresenter = new FilterPresenter({ filtersContainer: siteFilters, - pointModels, + filtersModel: filtersModel, + pointsModel: pointModels, }); pagePresenter.init(); diff --git a/src/mocs/filters.js b/src/mocs/filters.js deleted file mode 100644 index 417bf24..0000000 --- a/src/mocs/filters.js +++ /dev/null @@ -1,9 +0,0 @@ -import {filters} from './../utils/filters-utils.js'; - -const generateFilters = (points) => - Object.entries(filters).map(([filterType, filterPoints]) => ({ - type: filterType, - has: filterPoints(points).length > 0, - })); - -export {generateFilters}; diff --git a/src/model/filters-model.js b/src/model/filters-model.js new file mode 100644 index 0000000..b27afc0 --- /dev/null +++ b/src/model/filters-model.js @@ -0,0 +1,15 @@ +import Observable from './../framework/observable.js'; +import {FilterType} from './../const.js'; + +export default class FiltersModel extends Observable { + #filters = FilterType.EVERYTHING; + + get filters() { + return this.#filters; + } + + setFilters(updateType, filter) { + this.#filters = filter; + this._notify(updateType, filter); + } +} diff --git a/src/model/point-model.js b/src/model/point-model.js index 15902ef..11cbf15 100644 --- a/src/model/point-model.js +++ b/src/model/point-model.js @@ -1,10 +1,48 @@ +import Observable from './../framework/observable.js'; import {getRandomPoint} from './../mocs/route-point.js'; import {BasicValues} from './../const.js'; -export default class PointModel { +export default class PointModel extends Observable { #points = Array.from({length: BasicValues.COUNT_POINTS}, getRandomPoint); - getPoints() { + 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.find((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); + } } diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js index 60b1f95..5ff765a 100644 --- a/src/presenter/filter-presenter.js +++ b/src/presenter/filter-presenter.js @@ -1,17 +1,61 @@ -import {render} from './../framework/render.js'; +import {render, replace, remove} from './../framework/render.js'; import Filters from './../view/filters.js'; -import {generateFilters} from './../mocs/filters.js'; +import {UpdateType} from './../const.js'; +import {filters} from './../utils/filters-utils.js'; export default class FilterPresenter { #filtersContainer = null; - #filters = null; + #filtersModel = null; + #pointsModel = null; + #filterComponent = null; - constructor({filtersContainer, pointModels}) { + constructor({filtersContainer, filtersModel, pointsModel}) { this.#filtersContainer = filtersContainer; - this.#filters = generateFilters(pointModels.getPoints()); + this.#filtersModel = filtersModel; + this.#pointsModel = pointsModel; + + this.#pointsModel.addObserver(this.#handlerModelEvent); + this.#filtersModel.addObserver(this.#handlerModelEvent); + } + + get filters() { + const points = this.#pointsModel.points; + + return Object.entries(filters).map(([filterType, filterPoints]) => ({ + type: filterType, + has: filterPoints(points).length, + })); } init() { - render(new Filters({filters: this.#filters,}), this.#filtersContainer); + const currentFilters = this.filters; + const prevFilterComponent = this.#filterComponent; + + this.#filterComponent = new Filters({ + filters: currentFilters, + currentFilter: this.#filtersModel.filters, + onChangeFilters: this.#handlerFilters, + }); + + if (prevFilterComponent === null) { + render(this.#filterComponent, this.#filtersContainer); + return; + } + + replace(this.#filterComponent, prevFilterComponent); + remove(prevFilterComponent); } + + #handlerModelEvent = () => { + this.init(); + }; + + #handlerFilters = (filterType) => { + const currentFilter = filterType.slice(7); + if (this.#filtersModel.filters === currentFilter) { + return; + } + + this.#filtersModel.setFilters(UpdateType.MAJOR, currentFilter); + }; } diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 24ddd9c..9213042 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -1,7 +1,7 @@ import Point from './../view/point.js'; import EditForm from './../view/edit-form.js'; import {replace, render, remove} from './../framework/render.js'; -import {Mode} from './../const.js'; +import {Mode, UserAction, UpdateType} from './../const.js'; export default class PointPresenter { #currentPoint = null; @@ -83,7 +83,12 @@ export default class PointPresenter { }; #handlerFavoriteClick = () => { - this.#handlerDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + //this.#handlerDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); + this.#handlerDataChange( + UserAction.UPDATE_POINT, + UpdateType.MINOR, + {...this.#point, isFavorite: !this.#point.isFavorite}, + ); }; #handlerFormReset = () => { @@ -93,6 +98,10 @@ export default class PointPresenter { }; #handlerFormSubmit = (evt) => { - this.#handlerDataChange(evt); + this.#handlerDataChange( + UserAction.UPDATE_POINT, + UpdateType.MINOR, + evt, + ); }; } diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index 2aebcc0..5f48971 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -1,43 +1,62 @@ import Sorting from './../view/sorting.js'; import NoPoints from './../view/no-points.js'; import {render} from './../framework/render.js'; -import {updateItem} from './../utils/common.js'; -import {SortType} from './../const.js'; +import {SortType, UserAction, UpdateType} from './../const.js'; import {sortPointPrice, sortPointTime} from './../utils/points.js'; import PointPresenter from './point-presenter.js'; +import Observable from './../framework/observable.js'; +import {filters} from './../utils/filters-utils.js'; -export default class Presenter { +export default class Presenter extends Observable { #mainContainer = null; #pointModels = null; + #filtersModel = null; #currentSortType = SortType.DAY; - #primarySortPoints = []; - - #presenterPoints = []; #pointsCollection = new Map(); - constructor({mainContainer, pointModels}) { + constructor({mainContainer, pointModels, filtersModel}) { + super(); this.#mainContainer = mainContainer; this.#pointModels = pointModels; + this.#filtersModel = filtersModel; + + this.#pointModels.addObserver(this.#handlerModelEvent); + this.#filtersModel.addObserver(this.#handlerModelEvent); + } + + renderBoard() { + for (let i = 0; i < this.points.length; i++) { + this.#renderPoints(this.points[i]); + } } - #renderBoard() { - for (let i = 0; i < this.#presenterPoints.length; i++) { - this.#renderPoints(this.#presenterPoints[i]); + get points() { + const filterType = this.#filtersModel.filters; + const points = this.#pointModels.points; + const filteredPoints = filters[filterType](points); + + switch (this.#currentSortType) { + case SortType.TIME: + filteredPoints.sort(sortPointTime); + break; + case SortType.PRICE: + filteredPoints.sort(sortPointPrice); + break; } + + return filteredPoints; } init() { - this.#presenterPoints = [...this.#pointModels.getPoints()]; - this.#primarySortPoints = [...this.#pointModels.getPoints()]; this.#renderSorting(); - this.#renderBoard(); + this.renderBoard(); - if (this.#presenterPoints.length === 0) { + if (this.points.length === 0) { render(new NoPoints(), this.#mainContainer); } } - #clearPoints() { + clearBoard() { this.#pointsCollection.forEach((point) => point.destroy()); this.#pointsCollection.clear(); } @@ -49,7 +68,7 @@ export default class Presenter { #renderPoints(point) { const pointPresenter = new PointPresenter({ container: this.#mainContainer, - onDataChange: this.#handlerPointChange, + onDataChange: this.#handlerViewAction, onModeChange: this.#handlerModeChange, }); @@ -57,32 +76,40 @@ export default class Presenter { this.#pointsCollection.set(point.id, pointPresenter); } - #sortPoints(sortType) { + #handlerModeChange = () => { + this.#pointsCollection.forEach((point) => { + point.resetView(); + }); + }; - switch (sortType){ - case SortType.TIME: - this.#presenterPoints.sort(sortPointTime); + #handlerViewAction = (actionType, updateType, update) => { + switch (actionType) { + case UserAction.UPDATE_POINT: + this.#pointModels.updatePoint(updateType, update); break; - case SortType.PRICE: - this.#presenterPoints.sort(sortPointPrice); + case UserAction.ADD_POINT: + this.#pointModels.addPoint(updateType, update); + break; + case UserAction.DELETE_POINT: + this.#pointModels.deletePoint(updateType, update); break; - default: - this.#presenterPoints = [...this.#primarySortPoints]; } - - this.#currentSortType = sortType; - } - - #handlerPointChange = (updatedPoint) => { - this.#presenterPoints = updateItem(this.#presenterPoints, updatedPoint); - this.#primarySortPoints = updateItem(this.#presenterPoints, updatedPoint); - this.#pointsCollection.get(updatedPoint.id).init(updatedPoint); }; - #handlerModeChange = () => { - this.#pointsCollection.forEach((point) => { - point.resetView(); - }); + #handlerModelEvent = (updateType, data) => { + switch (updateType) { + case UpdateType.PATCH: + this.#pointsCollection.get(data.id).init(data); + break; + case UpdateType.MINOR: + this.clearBoard(); + this.renderBoard(); + break; + case UpdateType.MAJOR: + this.clearBoard(); + this.renderBoard(); + break; + } }; #handlerSortTypeChange = (sortType) => { @@ -90,8 +117,8 @@ export default class Presenter { return; } - this.#sortPoints(sortType); - this.#clearPoints(); - this.#renderBoard(); + this.#currentSortType = sortType; + this.clearBoard(); + this.renderBoard(); }; } diff --git a/src/utils/common.js b/src/utils/common.js index ce7ce3f..afedab6 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -2,11 +2,10 @@ import {BasicValues} from './../const.js'; const isEscapeKey = (evt) => evt.keyCode === BasicValues.ESCAPE_KEY; const getRandomArrayElement = (items) => items[Math.floor(Math.random() * items.length)]; -const updateItem = (items, update) => items.map((item) => item.id === update.id ? update : item); const capitalize = (word) => { const firstChar = word.charAt(0).toUpperCase(); const remainingChar = word.slice(1); return `${firstChar}${remainingChar}`; }; -export {isEscapeKey, getRandomArrayElement, updateItem, capitalize}; +export {isEscapeKey, getRandomArrayElement, capitalize}; diff --git a/src/view/filters.js b/src/view/filters.js index b2d2501..92189c4 100644 --- a/src/view/filters.js +++ b/src/view/filters.js @@ -1,9 +1,9 @@ import AbstractView from './../framework/view/abstract-view.js'; -const createFilters = (filter) => { +const createFilters = (filter, currentFilter) => { const createMarkup = (dataFilter) => Object.entries(dataFilter).map(([, data]) => `
- +
`).join(''); @@ -16,13 +16,25 @@ const createFilters = (filter) => { export default class Filters extends AbstractView { #filters = null; + #currentFilter = null; + #handlerFilters = null; - constructor({filters}) { + constructor({filters, currentFilter, onChangeFilters}) { super(); this.#filters = filters; + this.#currentFilter = currentFilter; + this.#handlerFilters = onChangeFilters; + this.element.addEventListener('click', this.#handlerFiltersChange); } get template() { - return createFilters(this.#filters); + return createFilters(this.#filters, this.#currentFilter); } + + #handlerFiltersChange = (evt) => { + const currentInput = evt.target; + if (currentInput.classList.contains('trip-filters__filter-input')) { + this.#handlerFilters(currentInput.id); + } + }; } From db8ca264b48a8af45bc1200b7130a4a0ab26cbea Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Fri, 4 Oct 2024 21:06:58 +0300 Subject: [PATCH 02/37] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D1=82=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82-=D0=B7=D0=B0=D0=B3=D0=BB=D1=83=D1=88=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/presenter.js | 25 ++++++++++++++++++------- src/view/no-points.js | 13 ++++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index 5f48971..fc68639 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -1,6 +1,6 @@ import Sorting from './../view/sorting.js'; import NoPoints from './../view/no-points.js'; -import {render} from './../framework/render.js'; +import {render, remove} from './../framework/render.js'; import {SortType, UserAction, UpdateType} from './../const.js'; import {sortPointPrice, sortPointTime} from './../utils/points.js'; import PointPresenter from './point-presenter.js'; @@ -13,6 +13,8 @@ export default class Presenter extends Observable { #filtersModel = null; #currentSortType = SortType.DAY; #pointsCollection = new Map(); + #noPoints = null; + #filterType = null; constructor({mainContainer, pointModels, filtersModel}) { super(); @@ -28,12 +30,16 @@ export default class Presenter extends Observable { for (let i = 0; i < this.points.length; i++) { this.#renderPoints(this.points[i]); } + + if (this.points.length === 0) { + this.#renderNoPoints(); + } } get points() { - const filterType = this.#filtersModel.filters; + this.#filterType = this.#filtersModel.filters; const points = this.#pointModels.points; - const filteredPoints = filters[filterType](points); + const filteredPoints = filters[this.#filterType](points); switch (this.#currentSortType) { case SortType.TIME: @@ -50,15 +56,15 @@ export default class Presenter extends Observable { init() { this.#renderSorting(); this.renderBoard(); - - if (this.points.length === 0) { - render(new NoPoints(), this.#mainContainer); - } } clearBoard() { this.#pointsCollection.forEach((point) => point.destroy()); this.#pointsCollection.clear(); + + if (this.#noPoints) { + remove(this.#noPoints); + } } #renderSorting() { @@ -76,6 +82,11 @@ export default class Presenter extends Observable { this.#pointsCollection.set(point.id, pointPresenter); } + #renderNoPoints() { + this.#noPoints = new NoPoints({filterType: this.#filterType}); + render(this.#noPoints, this.#mainContainer); + } + #handlerModeChange = () => { this.#pointsCollection.forEach((point) => { point.resetView(); diff --git a/src/view/no-points.js b/src/view/no-points.js index 3da0344..dd61e02 100644 --- a/src/view/no-points.js +++ b/src/view/no-points.js @@ -1,14 +1,21 @@ import AbstractView from './../framework/view/abstract-view.js'; import {FilterMessage} from './../const.js'; -const createNoPoints = () => `
+const createNoPoints = (filterType) => `

Trip events

-

${FilterMessage.EVERYTHING}

+

${FilterMessage[filterType]}

`; export default class noPoints extends AbstractView { + #filterType = null; + + constructor({filterType}) { + super(); + this.#filterType = filterType; + } + get template() { - return createNoPoints(); + return createNoPoints(this.#filterType.toUpperCase()); } } From 811ada661d91eaea0b56b41eee4f5ae248c193c0 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Thu, 10 Oct 2024 13:40:40 +0300 Subject: [PATCH 03/37] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D1=82=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=82=D0=BE=D1=87=D0=B5=D0=BA=20=D0=BC?= =?UTF-8?q?=D0=B0=D1=80=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 --- src/const.js | 25 +- src/main.js | 2 + src/model/point-model.js | 3 +- src/presenter/new-point-presenter.js | 62 +++++ src/presenter/point-presenter.js | 10 +- src/presenter/presenter.js | 37 ++- src/view/btn-new-point.js | 27 +++ src/view/create-form.js | 326 ++++++++++++++++----------- src/view/edit-form.js | 18 +- 9 files changed, 372 insertions(+), 138 deletions(-) create mode 100644 src/presenter/new-point-presenter.js create mode 100644 src/view/btn-new-point.js diff --git a/src/const.js b/src/const.js index d158e35..0d7e8ff 100644 --- a/src/const.js +++ b/src/const.js @@ -252,4 +252,27 @@ const UpdateType = { MAJOR: 'MAJOR', }; -export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType, UserAction, UpdateType}; +const NewPoint = { + event: 'taxi', + img: 'taxi', + basePrice: '', + offer: { + type: 'taxi', + offers: [ + { + id: 1, + title: 'Order Uber', + price: 20, + }, + ], + }, + isFavorite: false, + destination: { + id: 123, + description: 'this is destination', + name: '', + pictures: [{src: 'https://loremflickr.com/248/152?random=1', description: 'Oslo'}], + } +}; + +export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType, UserAction, UpdateType, NewPoint}; diff --git a/src/main.js b/src/main.js index 7bb2f22..b7b5b44 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,7 @@ import PointModel from './model/point-model.js'; import FiltersModel from './model/filters-model.js'; const siteBody = document.querySelector('.page-body'); +const headerMain = siteBody.querySelector('.trip-main'); const siteFilters = siteBody.querySelector('.trip-controls__filters'); const siteContainer = siteBody.querySelector('.trip-events'); @@ -14,6 +15,7 @@ const pagePresenter = new Presenter({ mainContainer: siteContainer, pointModels, filtersModel, + headerMain }); const filterPresenter = new FilterPresenter({ diff --git a/src/model/point-model.js b/src/model/point-model.js index 11cbf15..f27c2a5 100644 --- a/src/model/point-model.js +++ b/src/model/point-model.js @@ -31,7 +31,7 @@ export default class PointModel extends Observable { } deletePoint(updateType, update) { - const index = this.#points.find((point) => point.id === update.id); + const index = this.#points.findIndex((point) => point.id === update.id); if (index === -1) { throw new Error('Can\'t update unexisting point'); @@ -39,7 +39,6 @@ export default class PointModel extends Observable { this.#points = [ ...this.#points.slice(0, index), - update, ...this.#points.slice(index + 1) ]; diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js new file mode 100644 index 0000000..3c245e5 --- /dev/null +++ b/src/presenter/new-point-presenter.js @@ -0,0 +1,62 @@ +import NewForm from './../view/create-form.js'; +import {render, RenderPosition, remove} from './../framework/render.js'; +import {nanoid} from 'nanoid'; +import {UserAction, UpdateType} from '../const.js'; + +export default class NewPointPresenter { + #btnNewPoint = null; + #newForm = null; + #mainContainer = null; + #handlerDataChange = null; + #handlerDestroy = null; + + constructor({mainContainer, onDataChange, onDestroy}) { + this.#mainContainer = mainContainer; + this.#handlerDataChange = onDataChange; + this.#handlerDestroy = onDestroy; + } + + init() { + + if (this.#newForm !== null) { + return; + } + + this.#newForm = new NewForm({ + onFormSubmit: this.#handlerFormSubmit, + onFormReset: this.#handlerDeleteClick, + }); + + render(this.#newForm, this.#mainContainer, RenderPosition.AFTERBEGIN); + this.#newForm._restoreHandlers(); + this.#newForm._setDatepicker(); + this.#newForm.isOpen = true; + document.addEventListener('keydown', this.#newForm._handlerEscResetForm); + } + + destroy() { + if (this.#newForm === null) { + return; + } + + this.#handlerDestroy(); + + remove(this.#newForm); + this.#newForm = null; + } + + #handlerFormSubmit = (evt) => { + this.#handlerDataChange( + UserAction.ADD_POINT, + UpdateType.MINOR, + {id: nanoid(),...evt} + ); + this.destroy(); + }; + + #handlerDeleteClick = () => { + this.#newForm.isOpen = false; + this.#newForm._removeDatepicker(); + this.destroy(); + }; +} diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 9213042..91973c4 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -33,6 +33,7 @@ export default class PointPresenter { point: this.#point, onFormSubmit: this.#handlerFormSubmit, onFormReset: this.#handlerFormReset, + onFormDelete: this.#handlerDeletePoint, }); if (prevCurrentPoint === null || prevCurrentForm === null) { @@ -83,7 +84,6 @@ export default class PointPresenter { }; #handlerFavoriteClick = () => { - //this.#handlerDataChange({...this.#point, isFavorite: !this.#point.isFavorite}); this.#handlerDataChange( UserAction.UPDATE_POINT, UpdateType.MINOR, @@ -104,4 +104,12 @@ export default class PointPresenter { evt, ); }; + + #handlerDeletePoint = (evt) => { + this.#handlerDataChange( + UserAction.DELETE_POINT, + UpdateType.MINOR, + evt, + ); + }; } diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index fc68639..0499827 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -1,11 +1,13 @@ import Sorting from './../view/sorting.js'; import NoPoints from './../view/no-points.js'; import {render, remove} from './../framework/render.js'; -import {SortType, UserAction, UpdateType} from './../const.js'; +import {SortType, UserAction, UpdateType, FilterType} from './../const.js'; import {sortPointPrice, sortPointTime} from './../utils/points.js'; import PointPresenter from './point-presenter.js'; +import NewPointPresenter from './new-point-presenter.js'; import Observable from './../framework/observable.js'; import {filters} from './../utils/filters-utils.js'; +import BtnNewPoint from './../view/btn-new-point.js'; export default class Presenter extends Observable { #mainContainer = null; @@ -15,15 +17,30 @@ export default class Presenter extends Observable { #pointsCollection = new Map(); #noPoints = null; #filterType = null; + #newPointPresenter = null; + #btnNewPoint = null; + #headerMain = null; - constructor({mainContainer, pointModels, filtersModel}) { + constructor({mainContainer, pointModels, filtersModel, headerMain}) { super(); this.#mainContainer = mainContainer; this.#pointModels = pointModels; this.#filtersModel = filtersModel; + this.#headerMain = headerMain; this.#pointModels.addObserver(this.#handlerModelEvent); this.#filtersModel.addObserver(this.#handlerModelEvent); + + this.#newPointPresenter = new NewPointPresenter({ + mainContainer: this.#mainContainer, + onDataChange: this.#handlerViewAction, + onDestroy: this.#handlerNewFormClose, + }); + + this.#btnNewPoint = new BtnNewPoint({ + onClick: this.#handlerBtnNewPoint, + headerMain: this.#headerMain, + }); } renderBoard() { @@ -58,7 +75,14 @@ export default class Presenter extends Observable { this.renderBoard(); } + createTask() { + this.#currentSortType = SortType.DAY; + this.#filtersModel.setFilters(UpdateType.MAJOR, FilterType.EVERYTHING); + this.#newPointPresenter.init(); + } + clearBoard() { + this.#newPointPresenter.destroy(); this.#pointsCollection.forEach((point) => point.destroy()); this.#pointsCollection.clear(); @@ -88,6 +112,7 @@ export default class Presenter extends Observable { } #handlerModeChange = () => { + this.#newPointPresenter.destroy(); this.#pointsCollection.forEach((point) => { point.resetView(); }); @@ -132,4 +157,12 @@ export default class Presenter extends Observable { this.clearBoard(); this.renderBoard(); }; + + #handlerBtnNewPoint = () => { + this.createTask(); + }; + + #handlerNewFormClose = () => { + this.#btnNewPoint._handlerFormClose(); + }; } diff --git a/src/view/btn-new-point.js b/src/view/btn-new-point.js new file mode 100644 index 0000000..339c052 --- /dev/null +++ b/src/view/btn-new-point.js @@ -0,0 +1,27 @@ +import AbstractView from './../framework/view/abstract-view.js'; + +export default class BtnNewPoint extends AbstractView { + #handlerNewEventBtn = null; + #headerMain = null; + + constructor({onClick, headerMain}) { + super(); + this.#handlerNewEventBtn = onClick; + this.#headerMain = headerMain; + this.buttonNewEvent.addEventListener('click', this.#handlerBtnClick); + } + + get buttonNewEvent() { + return this.#headerMain.querySelector('.trip-main__event-add-btn'); + } + + #handlerBtnClick = (evt) => { + evt.preventDefault(); + this.#handlerNewEventBtn(); + this.buttonNewEvent.disabled = true; + }; + + _handlerFormClose() { + this.buttonNewEvent.disabled = false; + } +} diff --git a/src/view/create-form.js b/src/view/create-form.js index 084efed..e07cac5 100644 --- a/src/view/create-form.js +++ b/src/view/create-form.js @@ -1,128 +1,119 @@ -import AbstractStatefulView from './../framework/view/abstract-view.js'; -import {OFFER_TYPES, DESTINATION_CITIES} from './../const.js'; +import {humanizePointDueDate} from './../utils/points.js'; +import AbstractStatefulView from './../framework/view/abstract-stateful-view.js'; +import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, NewPoint} from './../const.js'; +import {isEscapeKey} from './../utils/common.js'; +import {capitalize} from './../utils/common.js'; +import flatpickr from 'flatpickr'; +import 'flatpickr/dist/flatpickr.min.css'; const createForm = (point) => { - const {event, img} = point; + const {basePrice, isEventType, isOffers, isCity, isDescription, isPictures} = point; + const {offers} = isOffers; + + const createImgMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `${value.description}`).join(''); + const createMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => ` +
+ + +
`).join(''); + const createEventType = (pointEvent, eventTypes) => eventTypes.map((type) => + `
+ + +
`).join(''); + const createCities = (cities) => cities.map((city) => ``).join(''); + return `
-
-
- - - -
-
- Event type - -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
-
- -
- - - - - - - -
- -
- - - — - - -
- -
- - -
- - - -
-
-
-

Offers

- -
-
- -
-

Destination

-

- -
-
-
-
-
-
`; +
+
+ + + +
+
+ Event type + ${createEventType(isEventType, EVENT_TYPES)} +
+
+
+ +
+ + + + ${createCities(CITIES)} + +
+ +
+ + + — + + +
+ +
+ + +
+ + + +
+
+ ${(offers.length > 0 ? ` +
+

Offers

+ +
+ ${createMarkup(offers)} +
+
` : ' ')} + +
+

Destination

+

${isDescription}

+ +
+
+ ${createImgMarkup(isPictures)} +
+
+
+
+ `; }; export default class NewForm extends AbstractStatefulView { #handlerFormClick = null; + #handlerFormReset = null; + #datepickerStart = null; + #datepickerEnd = null; + #point = null; - constructor({point, onFormSubmit}) { + constructor({onFormSubmit, onFormReset}) { super(); - this._setState(NewForm.parsePointToState(point)); + this.#point = NewPoint; + this._setState(NewForm.parsePointToState(this.#point)); this.#handlerFormClick = onFormSubmit; + this.#handlerFormReset = onFormReset; + } - this._restoreHandlers(); + get resetBtn() { + return this.element.querySelector('.event__reset-btn'); } get currentForm() { @@ -137,27 +128,55 @@ export default class NewForm extends AbstractStatefulView { return this.element.querySelector('.event__input--destination'); } - get offersBlock() { - return this.element.querySelector('.event__section--offers'); - } - get template() { return createForm(this._state); } + _removeDatepicker() { + if (this.#datepickerStart) { + this.#datepickerStart.destroy(); + this.#datepickerStart = null; + } + + if (this.#datepickerEnd) { + this.#datepickerEnd.destroy(); + this.#datepickerEnd = null; + } + } + _restoreHandlers() { - this.currentForm.addEventListener('submit', this.#handlerBtnClick); - this.eventTypeGroup.addEventListener('click', this.#handlerEventType); + this.currentForm.addEventListener('submit', this.#handlerBtnSubmit); + this.resetBtn.addEventListener('click', this._handlerResetForm); + this.eventTypeGroup.addEventListener('change', this.#handlerEventType); this.eventTypeCity.addEventListener('change', this.#handlerDestinationPoint); } - #handlerBtnClick = (evt) => { - evt.preventDefault(); + #handlerRemoveElements = () => { + this.#handlerFormReset(); + document.removeEventListener('keydown', this._handlerEscResetForm); + }; + #handlerBtnSubmit = () => { + this.updateElement(this._state.isOffers.offers = this.#creatingActualOffers()); this.#handlerFormClick(NewForm.parseStateToPoint(this._state)); + this._removeDatepicker(); + document.removeEventListener('keydown', this._handlerEscResetForm); + }; + + _handlerEscResetForm = (evt) => { + if (isEscapeKey(evt) && this.isOpen && !evt.target.classList.contains('event__input--time')) { + evt.preventDefault(); + this._handlerResetForm(); + } + }; + + _handlerResetForm = () => { + this.updateElement(NewForm.parsePointToState(this.#point)); + this.#handlerRemoveElements(); }; #handlerEventType = (evt) => { + this._removeDatepicker(); if (evt.target.classList.contains('event__type-input')) { evt.preventDefault(); this.updateElement({ @@ -165,9 +184,11 @@ export default class NewForm extends AbstractStatefulView { isOffers: OFFER_TYPES.find((item) => item.type === evt.target.value), }); } + this._setDatepicker(); }; #handlerDestinationPoint = (evt) => { + this._removeDatepicker(); evt.preventDefault(); DESTINATION_CITIES.find((item) => { if (item.name === evt.target.value) { @@ -178,8 +199,57 @@ export default class NewForm extends AbstractStatefulView { }); } }); + this._setDatepicker(); + }; + + #handlerOfferChecked = () => Array.from(this.element.querySelectorAll('.event__offer-checkbox')). + filter((item) => item.checked). + map((item) => item.getAttribute('id').at(-1)); + + #creatingActualOffers = () => { + const currentOffers = []; + this.#handlerOfferChecked().forEach((el) => { + currentOffers.push(OFFERS.find((item) => item.id === Number(el))); + }); + return currentOffers; + }; + + #handlerDateFromChange = ([selectedDate]) => { + this._state.dateFrom = humanizePointDueDate(selectedDate).datepicker; + this.#datepickerEnd.set('minDate', humanizePointDueDate(this._state.dateFrom).allDate); }; + #handlerDateToChange = ([selectedDate]) => { + this._state.dateTo = humanizePointDueDate(selectedDate).datepicker; + }; + + _setDatepicker() { + const [inputStartTime, inputEndTime] = this.element.querySelectorAll('.event__input--time'); + this.#datepickerStart = flatpickr(inputStartTime, { + defaultDate: 'today', + enableTime: true, + 'time_24hr': true, + dateFormat: 'y/m/d H:i', + minDate: humanizePointDueDate(this._state.dateFrom).allDate, + locale: { + firstDayOfWeek: 1, + }, + onClose: this.#handlerDateFromChange, + }); + + this.#datepickerEnd = flatpickr(inputEndTime, { + defaultDate: 'today', + enableTime: true, + 'time_24hr': true, + dateFormat: 'y/m/d H:i', + minDate: humanizePointDueDate(this._state.dateTo).allDate, + locale: { + firstDayOfWeek: 1, + }, + onClose: this.#handlerDateToChange, + }); + } + static parsePointToState(point) { return { ...point, @@ -194,12 +264,12 @@ export default class NewForm extends AbstractStatefulView { static parseStateToPoint(state) { const point = {...state}; - point.event = point.isEventType; - point.img = point.isEventType; - point.offer = point.isOffers; - point.destination.name = point.isCity; - point.destination.description = point.isDescription; - point.destination.pictures = point.isPictures; + point.event = state.isEventType; + point.img = state.isEventType; + point.offer = state.isOffers; + point.destination.name = state.isCity; + point.destination.description = state.isDescription; + point.destination.pictures = state.isPictures; delete point.isEventType; delete point.isOffers; diff --git a/src/view/edit-form.js b/src/view/edit-form.js index 351f922..4a21206 100644 --- a/src/view/edit-form.js +++ b/src/view/edit-form.js @@ -7,7 +7,7 @@ import 'flatpickr/dist/flatpickr.min.css'; import {isEscapeKey} from './../utils/common.js'; const createEditPoint = (point) => { - const {basePrice, event, dateFrom, dateTo, isEventType, isOffers, isCity, isDescription, isPictures} = point; + const {basePrice, dateFrom, dateTo, isEventType, isOffers, isCity, isDescription, isPictures} = point; const {offers} = isOffers; const createImgMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `${value.description}`).join(''); @@ -39,7 +39,7 @@ const createEditPoint = (point) => {
Event type - ${createEventType(event, EVENT_TYPES)} + ${createEventType(isEventType, EVENT_TYPES)}
@@ -103,22 +103,28 @@ const createEditPoint = (point) => { export default class EditForm extends AbstractStatefulView { #handlerFormClick = null; #handlerFormReset = null; + #handlerDeleteThisPoint = null; #datepickerStart = null; #datepickerEnd = null; #point = null; - constructor({point, onFormSubmit, onFormReset}) { + constructor({point, onFormSubmit, onFormReset, onFormDelete}) { super(); this.#point = point; this._setState(EditForm.parsePointToState(point)); this.#handlerFormClick = onFormSubmit; this.#handlerFormReset = onFormReset; + this.#handlerDeleteThisPoint = onFormDelete; } get rollupBtn() { return this.element.querySelector('.event__rollup-btn'); } + get deleteBtn() { + return this.element.querySelector('.event__reset-btn'); + } + get currentForm() { return this.element; } @@ -150,6 +156,7 @@ export default class EditForm extends AbstractStatefulView { _restoreHandlers() { this.currentForm.addEventListener('submit', this.#handlerBtnSubmit); this.rollupBtn.addEventListener('click', this.#handlerResetForm); + this.deleteBtn.addEventListener('click', this.#handlerDeletePoint); this.eventTypeGroup.addEventListener('change', this.#handlerEventType); this.eventTypeCity.addEventListener('change', this.#handlerDestinationPoint); } @@ -163,7 +170,6 @@ export default class EditForm extends AbstractStatefulView { this.updateElement(this._state.isOffers.offers = this.#creatingActualOffers()); this.#handlerFormClick(EditForm.parseStateToPoint(this._state)); this.#handlerRemoveElements(); - this._removeDatepicker(); }; _handlerEscResetForm = (evt) => { @@ -178,6 +184,10 @@ export default class EditForm extends AbstractStatefulView { this.#handlerRemoveElements(); }; + #handlerDeletePoint = () => { + this.#handlerDeleteThisPoint(this.#point); + }; + #handlerEventType = (evt) => { this._removeDatepicker(); if (evt.target.classList.contains('event__type-input')) { From e439ef7434713aabecbcae6ea5ce7a51e0404721 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Sun, 13 Oct 2024 11:19:16 +0300 Subject: [PATCH 04/37] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=81=D0=B1=D1=80=D0=BE=D1=81=20=D1=81?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 3 ++- src/presenter/filter-presenter.js | 12 ++++-------- src/presenter/new-point-presenter.js | 4 +++- src/presenter/presenter.js | 27 +++++++++++++++++++-------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main.js b/src/main.js index b7b5b44..07f4c81 100644 --- a/src/main.js +++ b/src/main.js @@ -15,13 +15,14 @@ const pagePresenter = new Presenter({ mainContainer: siteContainer, pointModels, filtersModel, - headerMain + headerMain, }); const filterPresenter = new FilterPresenter({ filtersContainer: siteFilters, filtersModel: filtersModel, pointsModel: pointModels, + presenter: pagePresenter, }); pagePresenter.init(); diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js index 5ff765a..e836b4c 100644 --- a/src/presenter/filter-presenter.js +++ b/src/presenter/filter-presenter.js @@ -8,14 +8,13 @@ export default class FilterPresenter { #filtersModel = null; #pointsModel = null; #filterComponent = null; + #presenter = null; - constructor({filtersContainer, filtersModel, pointsModel}) { + constructor({filtersContainer, filtersModel, pointsModel, presenter}) { this.#filtersContainer = filtersContainer; this.#filtersModel = filtersModel; this.#pointsModel = pointsModel; - - this.#pointsModel.addObserver(this.#handlerModelEvent); - this.#filtersModel.addObserver(this.#handlerModelEvent); + this.#presenter = presenter; } get filters() { @@ -46,16 +45,13 @@ export default class FilterPresenter { remove(prevFilterComponent); } - #handlerModelEvent = () => { - this.init(); - }; - #handlerFilters = (filterType) => { const currentFilter = filterType.slice(7); if (this.#filtersModel.filters === currentFilter) { return; } + this.#presenter.resetSortType(); this.#filtersModel.setFilters(UpdateType.MAJOR, currentFilter); }; } diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js index 3c245e5..81ef4cf 100644 --- a/src/presenter/new-point-presenter.js +++ b/src/presenter/new-point-presenter.js @@ -9,11 +9,13 @@ export default class NewPointPresenter { #mainContainer = null; #handlerDataChange = null; #handlerDestroy = null; + #presenter = null; - constructor({mainContainer, onDataChange, onDestroy}) { + constructor({mainContainer, onDataChange, onDestroy, presenter}) { this.#mainContainer = mainContainer; this.#handlerDataChange = onDataChange; this.#handlerDestroy = onDestroy; + this.#presenter = presenter; } init() { diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index 0499827..fad2981 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -1,6 +1,6 @@ import Sorting from './../view/sorting.js'; import NoPoints from './../view/no-points.js'; -import {render, remove} from './../framework/render.js'; +import {render, RenderPosition, remove} from './../framework/render.js'; import {SortType, UserAction, UpdateType, FilterType} from './../const.js'; import {sortPointPrice, sortPointTime} from './../utils/points.js'; import PointPresenter from './point-presenter.js'; @@ -20,6 +20,7 @@ export default class Presenter extends Observable { #newPointPresenter = null; #btnNewPoint = null; #headerMain = null; + #sorting = null; constructor({mainContainer, pointModels, filtersModel, headerMain}) { super(); @@ -35,6 +36,7 @@ export default class Presenter extends Observable { mainContainer: this.#mainContainer, onDataChange: this.#handlerViewAction, onDestroy: this.#handlerNewFormClose, + presenter: this, }); this.#btnNewPoint = new BtnNewPoint({ @@ -53,6 +55,14 @@ export default class Presenter extends Observable { } } + renderSorting() { + this.#sorting = new Sorting({ + onSortTypeChange: this.#handlerSortTypeChange, + }); + + render(this.#sorting, this.#mainContainer, RenderPosition.AFTERBEGIN); + } + get points() { this.#filterType = this.#filtersModel.filters; const points = this.#pointModels.points; @@ -71,12 +81,12 @@ export default class Presenter extends Observable { } init() { - this.#renderSorting(); this.renderBoard(); + this.renderSorting(); } createTask() { - this.#currentSortType = SortType.DAY; + this.resetSortType(); this.#filtersModel.setFilters(UpdateType.MAJOR, FilterType.EVERYTHING); this.#newPointPresenter.init(); } @@ -91,9 +101,11 @@ export default class Presenter extends Observable { } } - #renderSorting() { - render(new Sorting({onSortTypeChange: this.#handlerSortTypeChange}), this.#mainContainer); - } + resetSortType = () => { + remove(this.#sorting); + this.#currentSortType = SortType.DAY; + this.renderSorting(); + }; #renderPoints(point) { const pointPresenter = new PointPresenter({ @@ -154,8 +166,7 @@ export default class Presenter extends Observable { } this.#currentSortType = sortType; - this.clearBoard(); - this.renderBoard(); + this.#handlerModelEvent(UpdateType.MINOR); }; #handlerBtnNewPoint = () => { From 8a7b07404f3d41b4a09b20d8c881f0db7098709c Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Sun, 13 Oct 2024 11:54:46 +0300 Subject: [PATCH 05/37] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D1=81=D0=B1=D1=80=D0=BE=D1=81=20?= =?UTF-8?q?=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/filter-presenter.js | 1 + src/presenter/presenter.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/presenter/filter-presenter.js b/src/presenter/filter-presenter.js index e836b4c..1dcd9d5 100644 --- a/src/presenter/filter-presenter.js +++ b/src/presenter/filter-presenter.js @@ -52,6 +52,7 @@ export default class FilterPresenter { } this.#presenter.resetSortType(); + this.#presenter.renderSorting(); this.#filtersModel.setFilters(UpdateType.MAJOR, currentFilter); }; } diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index fad2981..e42bf7c 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -89,6 +89,7 @@ export default class Presenter extends Observable { this.resetSortType(); this.#filtersModel.setFilters(UpdateType.MAJOR, FilterType.EVERYTHING); this.#newPointPresenter.init(); + this.renderSorting(); } clearBoard() { @@ -104,7 +105,6 @@ export default class Presenter extends Observable { resetSortType = () => { remove(this.#sorting); this.#currentSortType = SortType.DAY; - this.renderSorting(); }; #renderPoints(point) { From 8d93fce592c0cbf1848aa6088e1bd6c9213372c5 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Sun, 13 Oct 2024 13:17:34 +0300 Subject: [PATCH 06/37] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D1=83,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- package.json | 1 + src/view/sorting.js | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03b0ae6..3cc5b84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "dayjs": "1.11.11", "flatpickr": "4.6.13", + "he": "1.2.0", "nanoid": "5.0.7" }, "devDependencies": { @@ -4639,7 +4640,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 29decf2..5a6e05f 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "dependencies": { "dayjs": "1.11.11", "flatpickr": "4.6.13", + "he": "1.2.0", "nanoid": "5.0.7" } } diff --git a/src/view/sorting.js b/src/view/sorting.js index 6ad36aa..f7f2543 100644 --- a/src/view/sorting.js +++ b/src/view/sorting.js @@ -43,11 +43,11 @@ export default class Sorting extends AbstractView { } #sortTypeChangeHandler = (evt) => { + evt.preventDefault(); const currentInput = evt.target.previousElementSibling; - if (!currentInput.classList.contains('trip-sort__input')) { + if (!evt.target.classList.contains('trip-sort__btn') || currentInput.disabled) { return; } - evt.preventDefault(); currentInput.checked = 'true'; this.#handlerSortTypeChange(currentInput.dataset.sortType); }; From 0cec836777fa1d96d92d3dee44ab9f79601ea71f Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Sun, 13 Oct 2024 13:29:03 +0300 Subject: [PATCH 07/37] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D1=83=20=D0=BF=D0=BE=20=D0=B4=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/presenter.js | 5 ++++- src/utils/points.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index e42bf7c..a86410b 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -2,7 +2,7 @@ import Sorting from './../view/sorting.js'; import NoPoints from './../view/no-points.js'; import {render, RenderPosition, remove} from './../framework/render.js'; import {SortType, UserAction, UpdateType, FilterType} from './../const.js'; -import {sortPointPrice, sortPointTime} from './../utils/points.js'; +import {sortPointPrice, sortPointTime, sortPointDate} from './../utils/points.js'; import PointPresenter from './point-presenter.js'; import NewPointPresenter from './new-point-presenter.js'; import Observable from './../framework/observable.js'; @@ -69,6 +69,9 @@ export default class Presenter extends Observable { const filteredPoints = filters[this.#filterType](points); switch (this.#currentSortType) { + case SortType.DAY: + filteredPoints.sort(sortPointDate); + break; case SortType.TIME: filteredPoints.sort(sortPointTime); break; diff --git a/src/utils/points.js b/src/utils/points.js index d20c9d1..493b670 100644 --- a/src/utils/points.js +++ b/src/utils/points.js @@ -53,4 +53,6 @@ const sortPointTime = (a, b) => dayjs(a.dateFrom).diff(dayjs(a.dateTo), TimeName const sortPointPrice = (a, b) => b.basePrice - a.basePrice; -export {humanizePointDueDate, isFuture, isPresent, isPast, sortPointPrice, sortPointTime}; +const sortPointDate = (a, b) => dayjs(a.dateFrom) - dayjs(b.dateFrom); + +export {humanizePointDueDate, isFuture, isPresent, isPast, sortPointPrice, sortPointTime, sortPointDate}; From 6b81ad0fefa68e40643985ac922041770d7a080d Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Tue, 22 Oct 2024 22:36:39 +0300 Subject: [PATCH 08/37] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D1=83=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/create-form.js | 44 +++++++++++++++++++++++++++++++------ src/view/edit-form.js | 48 ++++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/view/create-form.js b/src/view/create-form.js index e07cac5..ffc9fd8 100644 --- a/src/view/create-form.js +++ b/src/view/create-form.js @@ -7,7 +7,7 @@ import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; const createForm = (point) => { - const {basePrice, isEventType, isOffers, isCity, isDescription, isPictures} = point; + const {isPrice, isEventType, isOffers, isCity, isDescription, isPictures} = point; const {offers} = isOffers; const createImgMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `${value.description}`).join(''); @@ -48,7 +48,7 @@ const createForm = (point) => { - + ${createCities(CITIES)} @@ -56,10 +56,10 @@ const createForm = (point) => {
- + - +
@@ -67,7 +67,7 @@ const createForm = (point) => { Price € - +
@@ -124,10 +124,14 @@ export default class NewForm extends AbstractStatefulView { return this.element.querySelector('.event__type-group'); } - get eventTypeCity() { + get city() { return this.element.querySelector('.event__input--destination'); } + get price() { + return this.element.querySelector('.event__input--price'); + } + get template() { return createForm(this._state); } @@ -148,7 +152,8 @@ export default class NewForm extends AbstractStatefulView { this.currentForm.addEventListener('submit', this.#handlerBtnSubmit); this.resetBtn.addEventListener('click', this._handlerResetForm); this.eventTypeGroup.addEventListener('change', this.#handlerEventType); - this.eventTypeCity.addEventListener('change', this.#handlerDestinationPoint); + this.city.addEventListener('change', this.#handlerDestinationPoint); + this.price.addEventListener('change', this.#handlerPriceInput); } #handlerRemoveElements = () => { @@ -190,8 +195,11 @@ export default class NewForm extends AbstractStatefulView { #handlerDestinationPoint = (evt) => { this._removeDatepicker(); evt.preventDefault(); + + let currentValue; DESTINATION_CITIES.find((item) => { if (item.name === evt.target.value) { + currentValue = item.name; this.updateElement({ isCity: item.name, isDescription: item.description, @@ -199,9 +207,28 @@ export default class NewForm extends AbstractStatefulView { }); } }); + + if (evt.target.value !== currentValue) { + this.city.style = 'border: 1px solid red'; + this.city.value = ''; + this.city.setAttribute('placeholder', 'incorrect city'); + } + this._setDatepicker(); }; + #handlerPriceInput = (evt) => { + if (!Number(evt.target.value)) { + this.price.style = 'border: 1px solid red'; + this.price.value = ''; + this.price.setAttribute('placeholder', 'enter number'); + } else { + this.price.value = Math.floor(evt.target.value); + this.price.style = ''; + this._state.isPrice = evt.target.value; + } + }; + #handlerOfferChecked = () => Array.from(this.element.querySelectorAll('.event__offer-checkbox')). filter((item) => item.checked). map((item) => item.getAttribute('id').at(-1)); @@ -253,6 +280,7 @@ export default class NewForm extends AbstractStatefulView { static parsePointToState(point) { return { ...point, + isPrice: point.basePrice, isEventType: point.event, isOffers: point.offer, isCity: point.destination.name, @@ -264,6 +292,7 @@ export default class NewForm extends AbstractStatefulView { static parseStateToPoint(state) { const point = {...state}; + point.basePrice = state.isPrice; point.event = state.isEventType; point.img = state.isEventType; point.offer = state.isOffers; @@ -276,6 +305,7 @@ export default class NewForm extends AbstractStatefulView { delete point.isCity; delete point.isDescription; delete point.isPictures; + delete point.isPrice; return point; } } diff --git a/src/view/edit-form.js b/src/view/edit-form.js index 4a21206..3f4ea1d 100644 --- a/src/view/edit-form.js +++ b/src/view/edit-form.js @@ -7,7 +7,7 @@ import 'flatpickr/dist/flatpickr.min.css'; import {isEscapeKey} from './../utils/common.js'; const createEditPoint = (point) => { - const {basePrice, dateFrom, dateTo, isEventType, isOffers, isCity, isDescription, isPictures} = point; + const {isPrice, dateFrom, dateTo, isEventType, isOffers, isCity, isDescription, isPictures} = point; const {offers} = isOffers; const createImgMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `${value.description}`).join(''); @@ -48,7 +48,7 @@ const createEditPoint = (point) => { - + ${createCities(CITIES)} @@ -67,7 +67,7 @@ const createEditPoint = (point) => { Price € - + @@ -133,12 +133,16 @@ export default class EditForm extends AbstractStatefulView { return this.element.querySelector('.event__type-group'); } - get eventTypeCity() { + get template() { + return createEditPoint(this._state); + } + + get city() { return this.element.querySelector('.event__input--destination'); } - get template() { - return createEditPoint(this._state); + get price() { + return this.element.querySelector('.event__input--price'); } _removeDatepicker() { @@ -158,7 +162,8 @@ export default class EditForm extends AbstractStatefulView { this.rollupBtn.addEventListener('click', this.#handlerResetForm); this.deleteBtn.addEventListener('click', this.#handlerDeletePoint); this.eventTypeGroup.addEventListener('change', this.#handlerEventType); - this.eventTypeCity.addEventListener('change', this.#handlerDestinationPoint); + this.city.addEventListener('change', this.#handlerDestinationPoint); + this.price.addEventListener('change', this.#handlerPriceInput); } #handlerRemoveElements = () => { @@ -202,9 +207,11 @@ export default class EditForm extends AbstractStatefulView { #handlerDestinationPoint = (evt) => { this._removeDatepicker(); - evt.preventDefault(); + + let currentValue; DESTINATION_CITIES.find((item) => { if (item.name === evt.target.value) { + currentValue = item.name; this.updateElement({ isCity: item.name, isDescription: item.description, @@ -212,9 +219,30 @@ export default class EditForm extends AbstractStatefulView { }); } }); + + if (evt.target.value !== currentValue) { + this.city.style = 'border: 1px solid red'; + this.city.value = ''; + this.city.setAttribute('placeholder', 'incorrect city'); + } + this._setDatepicker(); }; + #handlerPriceInput = (evt) => { + if (!Number(evt.target.value)) { + this.price.style = 'border: 1px solid red'; + this.price.value = ''; + this.price.setAttribute('placeholder', 'enter number'); + } else { + this.price.value = Math.floor(evt.target.value); + this.price.style = ''; + this.updateElement({ + isPrice: evt.target.value, + }); + } + }; + #handlerOfferChecked = () => Array.from(this.element.querySelectorAll('.event__offer-checkbox')). filter((item) => item.checked). map((item) => item.getAttribute('id').at(-1)); @@ -264,8 +292,10 @@ export default class EditForm extends AbstractStatefulView { } static parsePointToState(point) { + return { ...point, + isPrice: point.basePrice, isEventType: point.event, isOffers: point.offer, isCity: point.destination.name, @@ -277,6 +307,7 @@ export default class EditForm extends AbstractStatefulView { static parseStateToPoint(state) { const point = {...state}; + point.basePrice = state.isPrice; point.event = state.isEventType; point.img = state.isEventType; point.offer = state.isOffers; @@ -289,6 +320,7 @@ export default class EditForm extends AbstractStatefulView { delete point.isCity; delete point.isDescription; delete point.isPictures; + delete point.isPrice; return point; } } From ca0656594ad5b1067228f5922d5b0dd1061db9e6 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Sun, 27 Oct 2024 13:32:20 +0300 Subject: [PATCH 09/37] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D1=83=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/style.css | 25 +++++++++++++++++------ src/const.js | 12 ++++++++++- src/presenter/new-point-presenter.js | 9 ++++++++- src/presenter/point-presenter.js | 21 +++++++++++++++++-- src/presenter/presenter.js | 1 + src/utils/common.js | 14 ++++++++++++- src/view/create-form.js | 30 +++++++++++++++------------- src/view/edit-form.js | 24 ++++++++++------------ src/view/tooltip.js | 17 ++++++++++++++++ 9 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 src/view/tooltip.js diff --git a/public/css/style.css b/public/css/style.css index c6c86ec..426865f 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -825,9 +825,22 @@ main { height: 152px; width: auto; border-radius: 4px; } - .event__photo:first-child { - padding-left: 20px; } - .event__photo:last-child { - padding-right: 20px; } - .event__photo:not(:last-child) { - margin-right: 5px; } +.event__photo:first-child { + padding-left: 20px; } +.event__photo:last-child { + padding-right: 20px; } +.event__photo:not(:last-child) { + margin-right: 5px; } +.tooltip { + position: absolute; + top: 100%; + left: 20%; + width: 150px; + padding: 8px 8px; + font-size: 12px; + line-height: 1.2; + color: #ffffff; + z-index: 1000; + background-color: #028ae4; + border-radius: 15px; +} diff --git a/src/const.js b/src/const.js index 0d7e8ff..7035ccf 100644 --- a/src/const.js +++ b/src/const.js @@ -275,4 +275,14 @@ const NewPoint = { } }; -export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType, UserAction, UpdateType, NewPoint}; +const TooltipLabel = { + CITY: 'CITY', + NUMBER: 'NUMBER', +}; + +const TooltipText = { + CITY: 'Please, select a city from the suggested list', + NUMBER: 'Please, enter a number', +}; + +export {BasicValues, OFFERS, EVENT_TYPES, CITIES, DESTINATION_CITIES, OFFER_TYPES, TimeNames, TimeFormat, POINTS, FilterType, FilterMessage, Mode, SortType, UserAction, UpdateType, NewPoint, TooltipText, TooltipLabel}; diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js index 81ef4cf..1020ee8 100644 --- a/src/presenter/new-point-presenter.js +++ b/src/presenter/new-point-presenter.js @@ -2,8 +2,9 @@ import NewForm from './../view/create-form.js'; import {render, RenderPosition, remove} from './../framework/render.js'; import {nanoid} from 'nanoid'; import {UserAction, UpdateType} from '../const.js'; +import Observable from './../framework/observable.js'; -export default class NewPointPresenter { +export default class NewPointPresenter extends Observable { #btnNewPoint = null; #newForm = null; #mainContainer = null; @@ -12,6 +13,7 @@ export default class NewPointPresenter { #presenter = null; constructor({mainContainer, onDataChange, onDestroy, presenter}) { + super(); this.#mainContainer = mainContainer; this.#handlerDataChange = onDataChange; this.#handlerDestroy = onDestroy; @@ -27,6 +29,7 @@ export default class NewPointPresenter { this.#newForm = new NewForm({ onFormSubmit: this.#handlerFormSubmit, onFormReset: this.#handlerDeleteClick, + onErrorForm: this.#handlerErrorForm, }); render(this.#newForm, this.#mainContainer, RenderPosition.AFTERBEGIN); @@ -61,4 +64,8 @@ export default class NewPointPresenter { this.#newForm._removeDatepicker(); this.destroy(); }; + + #handlerErrorForm = (container, thisTextContent) => { + this._notify(container, thisTextContent); + }; } diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 91973c4..d6de2cb 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -1,21 +1,29 @@ import Point from './../view/point.js'; import EditForm from './../view/edit-form.js'; +import Tooltip from './../view/tooltip.js'; import {replace, render, remove} from './../framework/render.js'; import {Mode, UserAction, UpdateType} from './../const.js'; +import Observable from './../framework/observable.js'; -export default class PointPresenter { +export default class PointPresenter extends Observable { #currentPoint = null; #currentForm = null; + #tooltip = null; #mainContainer = null; #handlerDataChange = null; #handlerModeChange = null; #point = null; #mode = Mode.DEFAULT; + #newPointPresenter = null; - constructor({container, onDataChange, onModeChange}) { + constructor({container, onDataChange, onModeChange, newPointPresenter}) { + super(); this.#mainContainer = container; this.#handlerDataChange = onDataChange; this.#handlerModeChange = onModeChange; + this.#newPointPresenter = newPointPresenter; + + this.#newPointPresenter.addObserver(this.#handlerErrorForm); } init(point) { @@ -34,6 +42,7 @@ export default class PointPresenter { onFormSubmit: this.#handlerFormSubmit, onFormReset: this.#handlerFormReset, onFormDelete: this.#handlerDeletePoint, + onErrorForm: this.#handlerErrorForm, }); if (prevCurrentPoint === null || prevCurrentForm === null) { @@ -105,6 +114,14 @@ export default class PointPresenter { ); }; + #handlerErrorForm = (container, thisTextContent) => { + this.#tooltip = new Tooltip({ + textContent: thisTextContent, + }); + + render(this.#tooltip, container); + }; + #handlerDeletePoint = (evt) => { this.#handlerDataChange( UserAction.DELETE_POINT, diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index a86410b..2880ec6 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -115,6 +115,7 @@ export default class Presenter extends Observable { container: this.#mainContainer, onDataChange: this.#handlerViewAction, onModeChange: this.#handlerModeChange, + newPointPresenter: this.#newPointPresenter, }); pointPresenter.init(point); diff --git a/src/utils/common.js b/src/utils/common.js index afedab6..5b812ab 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -8,4 +8,16 @@ const capitalize = (word) => { return `${firstChar}${remainingChar}`; }; -export {isEscapeKey, getRandomArrayElement, capitalize}; +const checkingForms = { + styleError: (input, container) => { + input.style = 'border: 1px solid red'; + input.value = ''; + container.style.setProperty('position', 'relative'); + }, + priceInputCorrect: (input, price) => { + input.value = Math.floor(price); + input.style = ''; + }, +}; + +export {isEscapeKey, getRandomArrayElement, capitalize, checkingForms}; diff --git a/src/view/create-form.js b/src/view/create-form.js index ffc9fd8..ba62a28 100644 --- a/src/view/create-form.js +++ b/src/view/create-form.js @@ -1,8 +1,7 @@ import {humanizePointDueDate} from './../utils/points.js'; import AbstractStatefulView from './../framework/view/abstract-stateful-view.js'; -import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, NewPoint} from './../const.js'; -import {isEscapeKey} from './../utils/common.js'; -import {capitalize} from './../utils/common.js'; +import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, NewPoint, TooltipLabel} from './../const.js'; +import {isEscapeKey, checkingForms, capitalize} from './../utils/common.js'; import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; @@ -48,7 +47,7 @@ const createForm = (point) => { - + ${createCities(CITIES)} @@ -67,7 +66,7 @@ const createForm = (point) => { Price € - + @@ -100,16 +99,18 @@ const createForm = (point) => { export default class NewForm extends AbstractStatefulView { #handlerFormClick = null; #handlerFormReset = null; + #handlerErrorForm = null; #datepickerStart = null; #datepickerEnd = null; #point = null; - constructor({onFormSubmit, onFormReset}) { + constructor({onFormSubmit, onFormReset, onErrorForm}) { super(); this.#point = NewPoint; this._setState(NewForm.parsePointToState(this.#point)); this.#handlerFormClick = onFormSubmit; this.#handlerFormReset = onFormReset; + this.#handlerErrorForm = onErrorForm; } get resetBtn() { @@ -209,9 +210,8 @@ export default class NewForm extends AbstractStatefulView { }); if (evt.target.value !== currentValue) { - this.city.style = 'border: 1px solid red'; - this.city.value = ''; - this.city.setAttribute('placeholder', 'incorrect city'); + checkingForms.styleError(this.city, this.city.parentElement); + this.#handlerErrorForm(this.city.parentElement, TooltipLabel.CITY); } this._setDatepicker(); @@ -219,12 +219,14 @@ export default class NewForm extends AbstractStatefulView { #handlerPriceInput = (evt) => { if (!Number(evt.target.value)) { - this.price.style = 'border: 1px solid red'; - this.price.value = ''; - this.price.setAttribute('placeholder', 'enter number'); + checkingForms.styleError(this.price, this.price.parentElement); + this.#handlerErrorForm(this.price.parentElement, TooltipLabel.NUMBER); } else { - this.price.value = Math.floor(evt.target.value); - this.price.style = ''; + //this.price.value = Math.floor(evt.target.value); + /*this.updateElement({ + isPrice: evt.target.value, + });*/ + checkingForms.priceInputCorrect(this.price, this.price.value); this._state.isPrice = evt.target.value; } }; diff --git a/src/view/edit-form.js b/src/view/edit-form.js index 3f4ea1d..330a235 100644 --- a/src/view/edit-form.js +++ b/src/view/edit-form.js @@ -1,10 +1,9 @@ import {humanizePointDueDate} from './../utils/points.js'; -import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES} from './../const.js'; -import {capitalize} from './../utils/common.js'; +import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, TooltipLabel} from './../const.js'; +import {isEscapeKey, capitalize, checkingForms} from './../utils/common.js'; import AbstractStatefulView from './../framework/view/abstract-stateful-view.js'; import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; -import {isEscapeKey} from './../utils/common.js'; const createEditPoint = (point) => { const {isPrice, dateFrom, dateTo, isEventType, isOffers, isCity, isDescription, isPictures} = point; @@ -48,7 +47,7 @@ const createEditPoint = (point) => { - + ${createCities(CITIES)} @@ -67,7 +66,7 @@ const createEditPoint = (point) => { Price € - + @@ -104,17 +103,19 @@ export default class EditForm extends AbstractStatefulView { #handlerFormClick = null; #handlerFormReset = null; #handlerDeleteThisPoint = null; + #handlerErrorForm = null; #datepickerStart = null; #datepickerEnd = null; #point = null; - constructor({point, onFormSubmit, onFormReset, onFormDelete}) { + constructor({point, onFormSubmit, onFormReset, onFormDelete, onErrorForm}) { super(); this.#point = point; this._setState(EditForm.parsePointToState(point)); this.#handlerFormClick = onFormSubmit; this.#handlerFormReset = onFormReset; this.#handlerDeleteThisPoint = onFormDelete; + this.#handlerErrorForm = onErrorForm; } get rollupBtn() { @@ -221,9 +222,8 @@ export default class EditForm extends AbstractStatefulView { }); if (evt.target.value !== currentValue) { - this.city.style = 'border: 1px solid red'; - this.city.value = ''; - this.city.setAttribute('placeholder', 'incorrect city'); + checkingForms.styleError(this.city, this.city.parentElement); + this.#handlerErrorForm(this.city.parentElement, TooltipLabel.CITY); } this._setDatepicker(); @@ -231,12 +231,10 @@ export default class EditForm extends AbstractStatefulView { #handlerPriceInput = (evt) => { if (!Number(evt.target.value)) { - this.price.style = 'border: 1px solid red'; - this.price.value = ''; - this.price.setAttribute('placeholder', 'enter number'); + checkingForms.styleError(this.price, this.price.parentElement); + this.#handlerErrorForm(this.price.parentElement, TooltipLabel.NUMBER); } else { this.price.value = Math.floor(evt.target.value); - this.price.style = ''; this.updateElement({ isPrice: evt.target.value, }); diff --git a/src/view/tooltip.js b/src/view/tooltip.js new file mode 100644 index 0000000..a7e77b0 --- /dev/null +++ b/src/view/tooltip.js @@ -0,0 +1,17 @@ +import AbstractView from './../framework/view/abstract-view.js'; +import {TooltipText} from './../const.js'; + +const createTooltip = (textContent) => `${TooltipText[textContent]}`; + +export default class Tooltip extends AbstractView { + #textContent = null; + + constructor({textContent}) { + super(); + this.#textContent = textContent; + } + + get template() { + return createTooltip(this.#textContent); + } +} From be0fc7c0125869810162243fc39dfa4f6575efd1 Mon Sep 17 00:00:00 2001 From: a_ubuntu Date: Fri, 1 Nov 2024 22:32:47 +0300 Subject: [PATCH 10/37] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D0=BF=D0=BE=D0=B2=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D0=BF=D1=83=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B2=20=D0=B4=D0=BE=D0=BF.=20=D0=BF=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/const.js | 1 + src/view/create-form.js | 42 ++++++++++++++++++++++++++++++++--------- src/view/edit-form.js | 34 +++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/const.js b/src/const.js index 7035ccf..4030b96 100644 --- a/src/const.js +++ b/src/const.js @@ -2,6 +2,7 @@ const BasicValues = { COUNT_POINTS: 3, TIME_STAMP: 10, ESCAPE_KEY: 27, + CHECKED: 'checked', }; const OFFERS = [ diff --git a/src/view/create-form.js b/src/view/create-form.js index ba62a28..bf53ed0 100644 --- a/src/view/create-form.js +++ b/src/view/create-form.js @@ -1,18 +1,18 @@ import {humanizePointDueDate} from './../utils/points.js'; import AbstractStatefulView from './../framework/view/abstract-stateful-view.js'; -import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, NewPoint, TooltipLabel} from './../const.js'; +import {OFFERS, EVENT_TYPES, OFFER_TYPES, CITIES, DESTINATION_CITIES, NewPoint, TooltipLabel, BasicValues} from './../const.js'; import {isEscapeKey, checkingForms, capitalize} from './../utils/common.js'; import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; const createForm = (point) => { - const {isPrice, isEventType, isOffers, isCity, isDescription, isPictures} = point; + const {isPrice, isEventType, isOffers, isCity, isDescription, isPictures, ...rest} = point; const {offers} = isOffers; const createImgMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `${value.description}`).join(''); const createMarkup = (dataMarkup) => Object.entries(dataMarkup).map(([, value]) => `
- +