Skip to content

Commit

Permalink
Merge pull request #8 from ershegm/module4-task2
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Apr 14, 2024
2 parents c3f3b98 + eac4033 commit 5c704a8
Show file tree
Hide file tree
Showing 19 changed files with 1,231 additions and 0 deletions.
362 changes: 362 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
"@babel/preset-env": "^7.24.4",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.1",
"eslint": "8.28.0",
"eslint-config-htmlacademy": "8.0.0",
"html-webpack-plugin": "^5.6.0",
"style-loader": "^4.0.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
Expand Down
28 changes: 28 additions & 0 deletions src/mock/destinations-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
DESCRIPTIONS,
MAX_IMAGES_COUNT,
} from '../const.js';

import {
getRandomArrayElement,
getRandomPositiveNumber
} from '../utils.js';


const generateDestination = (city) => {
return ({
id: crypto.randomUUID(),
description: getRandomArrayElement(DESCRIPTIONS),
name: city,
pictures: Array.from({
length: getRandomPositiveNumber(MAX_IMAGES_COUNT)
}, () => ({
'src': `https://loremflickr.com/248/152?random=${crypto.randomUUID()}`,
description: getRandomArrayElement(DESCRIPTIONS)
}))
});
};

export {
generateDestination,
};
26 changes: 26 additions & 0 deletions src/mock/event-points-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
Price,
} from '../const.js';

import {
getRandomDate,
getRandomPositiveNumber,
} from '../utils.js';

const generatePoint = (type, destinationId, offerIds) => {
const dateFrom = getRandomDate();
return {
id: crypto.randomUUID(),
basePrice: getRandomPositiveNumber(Price.MIN, Price.MAX),
dateFrom: dateFrom.toISOString(),
dateTo: getRandomDate(dateFrom).toISOString(),
destination: destinationId,
isFavorite: Boolean(getRandomPositiveNumber(0, 1)),
offers: offerIds,
type
};
};

export {
generatePoint,
};
17 changes: 17 additions & 0 deletions src/mock/offers-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
Price
} from '../const.js';

import {
getRandomPositiveNumber,
} from '../utils.js';

const generateOffer = (type) => ({
id: crypto.randomUUID(),
title: `Offer ${type}`,
price: getRandomPositiveNumber(Price.MIN, Price.MAX),
});

export {
generateOffer
};
30 changes: 30 additions & 0 deletions src/presenter/filters-presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
render
} from '../framework/render.js';
import FiltersView from '../view/filters-view.js';
import { FilterSettings } from '../const.js';
import { filterByType } from '../utils';

const filtersContainer = document.querySelector('.trip-controls__filters');

export default class FiltersPresenter {
#pointsModel = null;
#filters = [];

constructor({pointsModel}) {
this.#pointsModel = pointsModel;

this.#filters = Object.entries(filterByType)
.map(([type, filter]) => ({
...FilterSettings[type],
type,
disabled: filter(this.#pointsModel.get()).length === 0
}));
}

init() {
render(new FiltersView({
items: this.#filters
}), filtersContainer);
}
}
97 changes: 97 additions & 0 deletions src/presenter/points-presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
render,
replace
} from '../framework/render.js';
import EventsListView from '../view/events-list-view.js';
import EventsListEmptyView from '../view/events-list-empty-view.js';
import PointView from '../view/point-view.js';
import PointEditorView from '../view/editor-point-view.js';
import SortingView from '../view/create-sort-view.js';

export default class PointsPresenter {
#pointsContainer = null;
#pointsModel = null;
#destinationsModel = null;
#offersModel = null;
#points = [];

#listComponent = new EventsListView();
#sortingComponent = new SortingView();

constructor({
pointsContainer,
pointsModel,
offersModel,
destinationsModel
}) {
this.#pointsContainer = pointsContainer;
this.#pointsModel = pointsModel;
this.#offersModel = offersModel;
this.#destinationsModel = destinationsModel;
this.#points = [...this.#pointsModel.get()];
}

init() {
if (this.#points.length === 0) {
render(new EventsListEmptyView(), this.#pointsContainer);
return;
}

render(this.#sortingComponent, this.#pointsContainer);
render(this.#listComponent, this.#pointsContainer);

for (let i = 0; i < this.#points.length; i++) {
this.#renderPoint(this.#points[i]);
}
}

#renderPoint = (point) => {
const pointComponent = new PointView({
point,
destination: this.#destinationsModel.getById(point.destination),
offers: this.#offersModel.getByType(point.type),
onEditClick: pointEditHandler,
});

const pointEditorComponent = new PointEditorView({
point,
destination: this.#destinationsModel.getById(point.destination),
offers: this.#offersModel.getByType(point.type),
onCloseClick: pointCloseHandler,
onSubmitForm: pointSubmitHandler
});

const escKeyDownHandler = (evt) => {
if (evt.key === 'Escape') {
evt.preventDefault();
replaceEditorToPoint();
document.removeEventListener('keydown', escKeyDownHandler);
}
};

function replacePointToEditor() {
replace(pointEditorComponent, pointComponent);
}

function replaceEditorToPoint() {
replace(pointComponent, pointEditorComponent);
}

function pointEditHandler () {
replacePointToEditor();
document.addEventListener('keydown', escKeyDownHandler);
}

function pointSubmitHandler() {
replaceEditorToPoint();
document.removeEventListener('keydown', escKeyDownHandler);
}

function pointCloseHandler() {
replaceEditorToPoint();
document.removeEventListener('keydown', escKeyDownHandler);
}

render(pointComponent, this.#listComponent.element);
};
}
14 changes: 14 additions & 0 deletions src/presenter/trip-presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {
render,
RenderPosition
} from '../framework/render.js';

import TripInfoView from '../view/create-trip-view.js';

const tripMainContainer = document.querySelector('.trip-main');

export default class TripInfoPresenter {
init() {
render(new TripInfoView(), tripMainContainer, RenderPosition.AFTERBEGIN);
}
}
80 changes: 80 additions & 0 deletions src/service/mock-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
generateDestination,
} from '../mock/destinations-mock.js';
import {
generateOffer
} from '../mock/offers-mock.js';
import {
generatePoint
} from '../mock/event-points-mock.js';

import {
CITIES,
EVENT_TYPES,
MocksMaxCount
} from '../const.js';

import {
getRandomPositiveNumber,
getRandomArrayElement,
} from '../utils.js';


export default class MockService {
#destinations = [];
#offers = [];
#points = [];

constructor() {
this.#destinations = this.#generateDestinations();
this.#offers = this.#generateOffers();
this.#points = this.#generatePoints();
}

get points() {
return this.#points;
}

get destinations() {
return this.#destinations;
}

get offers() {
return this.#offers;
}

#generateDestinations(){
return CITIES.map(generateDestination);
}

#generateOffers(){
return EVENT_TYPES.map((type) => ({
type,
offers: Array.from({
length: getRandomPositiveNumber(1, MocksMaxCount.OFFERS)
}, () => generateOffer(type))
}));
}

#generatePoints() {
return Array.from({
length: getRandomPositiveNumber(0, MocksMaxCount.POINTS)
}, () => {
const type = getRandomArrayElement(EVENT_TYPES);
const destination = getRandomArrayElement(this.#destinations);
const offersByType = this.#offers.find((offerByType) => offerByType.type === type);

const randomOffers = Array.from({
length: getRandomPositiveNumber(0, offersByType.offers.length)
}, () => getRandomArrayElement(offersByType.offers));

const uniqueOffersIds = randomOffers.reduce((acc, current) => {
if (acc.includes(current.id)) {
return acc;
}
return [...acc, current.id];
}, []);
return generatePoint(type, destination.id, uniqueOffersIds);
});
}
}
21 changes: 21 additions & 0 deletions src/view/Item-list-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AbstractView from '../framework/view/abstract-view.js';

export default class ItemListView extends AbstractView {
_items = [];
_handleItemChange = null;

constructor ({
items,
onItemChange
}) {
super();
this._items = items;
this._handleItemChange = onItemChange;
this.element.addEventListener('change', this.#itemChangeHandler);
}

#itemChangeHandler = (evt) => {
evt.preventDefault();
this._handleItemChange?.(evt.target.dataset.item);
};
}
27 changes: 27 additions & 0 deletions src/view/create-sort-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SORTING_COLUMNS } from '../const.js';
import ItemListView from './item-list-view.js';

function createSortingItemTemplate(column) {
const { type, label, active, defaultSelected } = column;
return `
<div class="trip-sort__item trip-sort__item--${type}">
<input id="sort-${type}" class="trip-sort__input visually-hidden" type="radio" name="trip-sort" value="${type}" ${!active ? 'disabled' : ''} ${defaultSelected ? 'checked' : ''}>
<label class="trip-sort__btn" for="sort-${type}">${label}</label>
</div>`;
}

function createSortingTemplate() {
return `<form class="trip-events__trip-sort trip-sort" action="#" method="get">
${SORTING_COLUMNS.map(createSortingItemTemplate).join('')}
</form>`;
}

export default class SortingView extends ItemListView {
constructor() {
super({ items: SORTING_COLUMNS });
}

get template() {
return createSortingTemplate();
}
}
21 changes: 21 additions & 0 deletions src/view/create-trip-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AbstractView from '../framework/view/abstract-view.js';

function createTripInfoTemplate() {
return (
`<section class="trip-main__trip-info trip-info">
<div class="trip-info__main">
<h1 class="trip-info__title">Amsterdam &mdash; Chamonix &mdash; Geneva</h1>
<p class="trip-info__dates">Mar 18&nbsp;&mdash;&nbsp;20</p>
</div>
<p class="trip-info__cost">
Total: &euro;&nbsp;<span class="trip-info__cost-value">1230</span>
</p>
</section>`
);
}

export default class TripInfoView extends AbstractView {
get template() {
return createTripInfoTemplate();
}
}
Loading

0 comments on commit 5c704a8

Please sign in to comment.