diff --git a/package-lock.json b/package-lock.json index 162c06a..7bd2dd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,11 @@ "@babel/preset-env": "7.21.4", "babel-loader": "9.1.2", "copy-webpack-plugin": "11.0.0", + "css-loader": "6.7.3", "eslint": "8.38.0", "eslint-config-htmlacademy": "9.0.0", "html-webpack-plugin": "5.5.1", + "style-loader": "3.3.2", "webpack": "5.79.0", "webpack-cli": "5.0.1", "webpack-dev-server": "4.13.3" @@ -3170,6 +3172,44 @@ "node": ">= 8" } }, + "node_modules/css-loader": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.19", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -3198,6 +3238,18 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/dayjs": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", @@ -4881,6 +4933,18 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -5720,6 +5784,24 @@ "multicast-dns": "cli.js" } }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6231,6 +6313,112 @@ "node": ">=8" } }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7117,6 +7305,15 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -7277,6 +7474,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-loader": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.2.tgz", + "integrity": "sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index b1354d1..39ee690 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "@babel/preset-env": "7.21.4", "babel-loader": "9.1.2", "copy-webpack-plugin": "11.0.0", + "css-loader": "6.7.3", "eslint": "8.38.0", "eslint-config-htmlacademy": "9.0.0", "html-webpack-plugin": "5.5.1", + "style-loader": "3.3.2", "webpack": "5.79.0", "webpack-cli": "5.0.1", "webpack-dev-server": "4.13.3" diff --git a/src/model/events-model.js b/src/model/events-model.js index 4c99018..7cc670a 100644 --- a/src/model/events-model.js +++ b/src/model/events-model.js @@ -1,15 +1,15 @@ import {mockDestinations, mockEvents, mockOffers} from '../mock/event'; export default class EventsModel { - getEvents() { + get events() { return [...mockEvents]; } - getDestinations() { + get destinations() { return [...mockDestinations]; } - getOffers() { + get offers() { return [...mockOffers]; } } diff --git a/src/presenter/presenter.js b/src/presenter/presenter.js index 148f792..989daca 100644 --- a/src/presenter/presenter.js +++ b/src/presenter/presenter.js @@ -1,4 +1,4 @@ -import {render} from '../render'; +import {render, replace} from '../framework/render'; import EventItem from '../view/event-item'; import EventList from '../view/event-list'; import Filters from '../view/filters'; @@ -6,26 +6,61 @@ import Sorting from '../view/sorting'; import EditForm from '../view/edit-form'; export default class Presenter { - eventListComponent = new EventList(); - eventsContainer = document.querySelector('.trip-events'); - filterContainer = document.querySelector('.trip-controls__filters'); + #eventListComponent = new EventList(); + #eventsContainer = document.querySelector('.trip-events'); + #filterContainer = document.querySelector('.trip-controls__filters'); + #eventsModel = null; + #events = null; + #destinations = null; + #offers = null; constructor({eventsModel}) { - this.eventsModel = eventsModel; + this.#eventsModel = eventsModel; } init() { - this.events = this.eventsModel.getEvents(); - this.destinations = this.eventsModel.getDestinations(); - this.offers = this.eventsModel.getOffers(); + this.#events = this.#eventsModel.events; + this.#destinations = this.#eventsModel.destinations; + this.#offers = this.#eventsModel.offers; - render(new Filters(), this.filterContainer); - render(new Sorting(), this.eventsContainer); - render(this.eventListComponent, this.eventsContainer); - render(new EditForm({event:this.events[0], destinations: this.destinations, offers: this.offers}), this.eventListComponent.getElement()); + render(new Filters(), this.#filterContainer); + render(new Sorting(), this.#eventsContainer); + render(this.#eventListComponent, this.#eventsContainer); - for (let i = 1; i < this.events.length; i++) { - render(new EventItem({event:this.events[i], destinations: this.destinations, offers: this.offers}), this.eventListComponent.getElement()); + for (let i = 0; i < this.#events.length; i++) { + this.#renderEvent(this.#events[i]); } } + + #renderEvent(event) { + const escKeyDownHandler = (evt) => { + if (evt.key === 'Escape') { + evt.preventDefault(); + replaceFromEditToItem(); + document.removeEventListener('keydown', escKeyDownHandler); + } + }; + + const editForm = new EditForm({event, destinations: this.#destinations, offers: this.#offers, submitHandler: () => { + replaceFromEditToItem(); + document.removeEventListener('keydown', escKeyDownHandler); + }, clickHandler: () => { + replaceFromEditToItem(); + document.removeEventListener('keydown', escKeyDownHandler); + }}); + const eventItem = new EventItem({event, destinations: this.#destinations, offers: this.#offers, clickHandler: () => { + replaceFromItemToEdit(); + document.addEventListener('keydown', escKeyDownHandler); + }}); + + function replaceFromEditToItem() { + replace(eventItem, editForm); + } + + function replaceFromItemToEdit() { + replace(editForm,eventItem); + } + + render(eventItem, this.#eventListComponent.element); + } } diff --git a/src/render.js b/src/render.js deleted file mode 100644 index 8c22c92..0000000 --- a/src/render.js +++ /dev/null @@ -1,19 +0,0 @@ -const RenderPosition = { - BEFOREBEGIN: 'beforebegin', - AFTERBEGIN: 'afterbegin', - BEFOREEND: 'beforeend', - AFTEREND: 'afterend', -}; - -function createElement(template) { - const newElement = document.createElement('div'); - newElement.innerHTML = template; - - return newElement.firstElementChild; -} - -function render(component, container, place = RenderPosition.BEFOREEND) { - container.insertAdjacentElement(place, component.getElement()); -} - -export {RenderPosition, createElement, render}; diff --git a/src/view/create-form.js b/src/view/create-form.js index 4d06fc0..9ed2144 100644 --- a/src/view/create-form.js +++ b/src/view/create-form.js @@ -1,4 +1,4 @@ -import {createElement} from '../render'; +import AbstractView from '../framework/view/abstract-view'; function getCreateFormTemplate() { return ` @@ -167,20 +167,8 @@ function getCreateFormTemplate() { `; } -export default class CreateForm { - getTemplate() { +export default class CreateForm extends AbstractView { + get template() { return getCreateFormTemplate(); } - - getElement() { - if(!this.element) { - this.element = createElement(this.getTemplate()); - } - - return this.element; - } - - removeElement() { - this.element = null; - } } diff --git a/src/view/edit-form.js b/src/view/edit-form.js index 8b7768f..f771b92 100644 --- a/src/view/edit-form.js +++ b/src/view/edit-form.js @@ -1,5 +1,5 @@ -import {createElement} from '../render'; import {formatDate, getDestination, getEventIconUrl, getTypeOffers} from '../utils/utils'; +import AbstractView from '../framework/view/abstract-view'; function getOfferTemplate(offer, eventOffers) { @@ -141,26 +141,24 @@ function getEditFormTemplate(event, destinations, offers) { `; } -export default class EditForm { - constructor({event, destinations, offers}) { - this.event = event; - this.destinations = destinations; - this.offers = offers; +export default class EditForm extends AbstractView { + #event = null; + #destinations = null; + #offers = null; + + constructor({event, destinations, offers, submitHandler, clickHandler}) { + super(); + this.#event = event; + this.#destinations = destinations; + this.#offers = offers; + this.element.querySelector('.event.event--edit').addEventListener('submit', submitHandler); + this.element.querySelector('.event__rollup-btn').addEventListener('click', (clickEvent) => { + clickEvent.preventDefault(); + clickHandler(); + }); } - getTemplate() { - return getEditFormTemplate(this.event, this.destinations, this.offers); - } - - getElement() { - if (!this.element) { - this.element = createElement(this.getTemplate()); - } - - return this.element; - } - - removeElement() { - this.element = null; + get template() { + return getEditFormTemplate(this.#event, this.#destinations, this.#offers); } } diff --git a/src/view/event-item.js b/src/view/event-item.js index 811cc7b..2b26d21 100644 --- a/src/view/event-item.js +++ b/src/view/event-item.js @@ -1,4 +1,3 @@ -import {createElement} from '../render'; import { formatDate, getDuration, @@ -8,6 +7,7 @@ import { getOffer, getTypeOffers } from '../utils/utils'; +import AbstractView from '../framework/view/abstract-view'; function getOfferTemplate(id, type, offers){ const typeOffers = getTypeOffers(type, offers) || []; @@ -46,7 +46,7 @@ function getEventItemTemplate(event, destinations, offers) {
— - +
${duration}
@@ -71,26 +71,23 @@ function getEventItemTemplate(event, destinations, offers) { `; } -export default class EventItem { - constructor({event, destinations, offers}) { - this.event = event; - this.destinations = destinations; - this.offers = offers; - } - - getTemplate() { - return getEventItemTemplate(this.event, this.destinations, this.offers); - } - - getElement() { - if (!this.element) { - this.element = createElement(this.getTemplate()); - } +export default class EventItem extends AbstractView { + #event = null; + #destinations = null; + #offers = null; - return this.element; + constructor({event, destinations, offers, clickHandler}) { + super(); + this.#event = event; + this.#destinations = destinations; + this.#offers = offers; + this.element.querySelector('.event__rollup-btn').addEventListener('click', (eventClick) => { + eventClick.preventDefault(); + clickHandler(); + }); } - removeElement() { - this.element = null; + get template() { + return getEventItemTemplate(this.#event, this.#destinations, this.#offers); } } diff --git a/src/view/event-list.js b/src/view/event-list.js index a4f73f9..c5acfd9 100644 --- a/src/view/event-list.js +++ b/src/view/event-list.js @@ -1,4 +1,4 @@ -import {createElement} from '../render'; +import AbstractView from '../framework/view/abstract-view'; function getEventListTemplate() { return ` @@ -6,20 +6,8 @@ function getEventListTemplate() { `; } -export default class EventList { - getTemplate() { +export default class EventList extends AbstractView { + get template() { return getEventListTemplate(); } - - getElement() { - if(!this.element) { - this.element = createElement(this.getTemplate()); - } - - return this.element; - } - - removeElement() { - this.element = null; - } } diff --git a/src/view/filters.js b/src/view/filters.js index 5766620..4de3dc6 100644 --- a/src/view/filters.js +++ b/src/view/filters.js @@ -1,4 +1,4 @@ -import {createElement} from '../render'; +import AbstractView from '../framework/view/abstract-view'; function filtersTemplate() { return ` @@ -28,20 +28,8 @@ function filtersTemplate() { `; } -export default class Filters { - getTemplate() { +export default class Filters extends AbstractView { + get template() { return filtersTemplate(); } - - getElement() { - if(!this.element) { - this.element = createElement(this.getTemplate()); - } - - return this.element; - } - - removeElement() { - this.element = null; - } } diff --git a/src/view/sorting.js b/src/view/sorting.js index 0fc8a53..b28d74a 100644 --- a/src/view/sorting.js +++ b/src/view/sorting.js @@ -1,4 +1,4 @@ -import {createElement} from '../render'; +import AbstractView from '../framework/view/abstract-view'; function getSortingTemplate() { return ` @@ -31,20 +31,8 @@ function getSortingTemplate() { `; } -export default class Sorting { - getTemplate() { +export default class Sorting extends AbstractView { + get template() { return getSortingTemplate(); } - - getElement() { - if(!this.element) { - this.element = createElement(this.getTemplate()); - } - - return this.element; - } - - removeElement() { - this.element = null; - } } diff --git a/webpack.config.js b/webpack.config.js index 9af53f0..bcb70fc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -36,6 +36,10 @@ module.exports = { }, }, }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'] + }, ] } };