diff --git a/package-lock.json b/package-lock.json index 0629cd7..065aa68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "big-trip", "version": "22.0.0", "dependencies": { - "dayjs": "1.11.13" + "dayjs": "1.11.13", + "flatpickr": "^4.6.13" }, "devDependencies": { "@babel/core": "7.25.2", @@ -19,7 +20,7 @@ "eslint": "8.57.1", "eslint-config-htmlacademy": "10.0.1", "html-webpack-plugin": "5.6.0", - "style-loader": "^4.0.0", + "style-loader": "4.0.0", "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "5.1.0" @@ -4933,6 +4934,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", + "license": "MIT" + }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", diff --git a/package.json b/package.json index 754e0c4..2cb7146 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "node": "20" }, "dependencies": { - "dayjs": "1.11.13" + "dayjs": "1.11.13", + "flatpickr": "4.6.13" } } diff --git a/src/constants.js b/src/constants.js index ba8bf7e..10464c9 100644 --- a/src/constants.js +++ b/src/constants.js @@ -71,13 +71,14 @@ export const TRIP_SORT_ITEMS = [ }, ]; -export const DATE_TIME_FORMAT = { +export const DateFormat = { SHORT_DATE: 'MMM D', DATE_AND_TIME: 'DD/MM/YY HH:mm', TIME: 'HH:mm', D_H_M_DURATION: 'DD[D] HH[H] mm[M]', H_M_DURATION: 'HH[H] mm[M]', - M_DURATION: 'mm[M]' + M_DURATION: 'mm[M]', + DATE_PICKED: 'd/m/y H:i' }; export const EVENTS_MESSAGE = { diff --git a/src/util/utils.js b/src/util/utils.js index ffa5e95..b0a3954 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -1,6 +1,6 @@ import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; -import { DATE_TIME_FORMAT } from '../constants.js'; +import { DateFormat } from '../constants.js'; export const getRandomInteger = (min = 1, max = 100) => Math.round(Math.random() * Math.abs(max - min)) + min; @@ -14,14 +14,14 @@ export const calculateDuration = (start, end) => dayjs.duration(dayjs(end).diff( export const convertDuration = (value) => { if (value.get('day')) { - return value.format(DATE_TIME_FORMAT.D_H_M_DURATION); + return value.format(DateFormat.D_H_M_DURATION); } if (!value.get('day') && value.get('hour')) { - return value.format(DATE_TIME_FORMAT.H_M_DURATION); + return value.format(DateFormat.H_M_DURATION); } - return value.format(DATE_TIME_FORMAT.M_DURATION); + return value.format(DateFormat.M_DURATION); }; export const isDateFuture = (start) => dayjs().isBefore(start); diff --git a/src/view/event-editor-view.js b/src/view/event-editor-view.js index 35ad2c5..f63b998 100644 --- a/src/view/event-editor-view.js +++ b/src/view/event-editor-view.js @@ -1,6 +1,8 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view'; import { convertDate, getCapitalized } from '../util/utils.js'; -import { DATE_TIME_FORMAT, EVENT_TYPES } from '../constants.js'; +import { DateFormat, EVENT_TYPES } from '../constants.js'; +import flatpickr from 'flatpickr'; +import 'flatpickr/dist/flatpickr.min.css'; const createEventTypeItemTemplate = (type, pointId) => ` ${EVENT_TYPES.map((eventType) => (` @@ -89,8 +91,8 @@ const createEventEditorTemplate = (point, offers, destinations)=> { const { description, name, pictures } = eventDestination || {}; const pointId = point.id || 0; - const startTime = convertDate(dateFrom, DATE_TIME_FORMAT.DATE_AND_TIME); - const endTime = convertDate(dateTo, DATE_TIME_FORMAT.DATE_AND_TIME); + const startTime = convertDate(dateFrom, DateFormat.DATE_AND_TIME); + const endTime = convertDate(dateTo, DateFormat.DATE_AND_TIME); return `
  • @@ -162,6 +164,8 @@ export default class EventEditorView extends AbstractStatefulView { #handleFormSubmit = null; #handleEditClick = null; + #dateFromPicker = null; + #dateToPicker = null; constructor({ point, offers, destinations, onEditClick, onFormSubmit }) { super(); @@ -197,6 +201,21 @@ export default class EventEditorView extends AbstractStatefulView { this.element.querySelector('.event__input--destination').addEventListener('change', this.#changeDestinationHandler); this.element.querySelector('.event__available-offers')?.addEventListener('change', this.#changeAvailableOffersHandler); this.element.querySelector('.event__field-group--price').addEventListener('input', this.#changePriceHandler); + + this.#setDatePicker(); + } + + removeElement() { + super.removeElement(); + + if (this.#dateFromPicker) { + this.#dateFromPicker.destroy(); + this.#dateFromPicker = null; + } + if (this.#dateToPicker) { + this.#dateToPicker.destroy(); + this.#dateToPicker = null; + } } #formSubmitHandler = (event) => { @@ -231,4 +250,41 @@ export default class EventEditorView extends AbstractStatefulView { basePrice: parseInt(event.target.value, 10) }); }; + + #setDatePicker = () => { + const startTime = this.element.querySelector(`#event-start-time-${this._state.id}`); + const endTime = this.element.querySelector(`#event-end-time-${this._state.id}`); + + const generalFlatpickrConfig = { + enableTime: true, + 'time_24hr': true, + dateFormat: DateFormat.DATE_PICKED + }; + + this.#dateFromPicker = flatpickr( + startTime, + { + ...generalFlatpickrConfig, + maxDate: this._state.dateTo, + onChange: this.#changeDateHandler('dateFrom'), + onClose: (_, userDate) => this.#dateToPicker.set('minDate', userDate) + } + ); + + this.#dateToPicker = flatpickr( + endTime, + { + ...generalFlatpickrConfig, + maxDate: this._state.dateFrom, + onChange: this.#changeDateHandler('dateTo'), + onClose: (_, userDate) => this.#dateFromPicker.set('maxDate', userDate) + } + ); + }; + + #changeDateHandler = (name) => ([userDate]) => { + this._setState({ + [name]: userDate + }); + }; } diff --git a/src/view/events-item-view.js b/src/view/events-item-view.js index 4c83fc7..4c7f933 100644 --- a/src/view/events-item-view.js +++ b/src/view/events-item-view.js @@ -1,6 +1,6 @@ import AbstractView from '../framework/view/abstract-view.js'; import { calculateDuration, convertDate, convertDuration, getCapitalized } from '../util/utils.js'; -import { DATE_TIME_FORMAT } from '../constants.js'; +import { DateFormat } from '../constants.js'; const createEventSelectedOffersTemplate = (selectedOffers) => { if (selectedOffers.length === 0) { @@ -25,9 +25,9 @@ const createEventItemTemplate = (point, offers, destinations) => { const selectedOffers = defaultOffers.filter((defaultOffer) => point.offers.includes(defaultOffer.id)); const destination = destinations.find((item) => item.id === point.destination); - const startDate = convertDate(dateFrom, DATE_TIME_FORMAT.SHORT_DATE); - const startTime = convertDate(dateFrom, DATE_TIME_FORMAT.TIME); - const endTime = convertDate(dateTo, DATE_TIME_FORMAT.TIME); + const startDate = convertDate(dateFrom, DateFormat.SHORT_DATE); + const startTime = convertDate(dateFrom, DateFormat.TIME); + const endTime = convertDate(dateTo, DateFormat.TIME); const duration = convertDuration(calculateDuration(dateFrom, dateTo)); const favorite = isFavorite ? 'event__favorite-btn--active' : '';