UNPKG

@progressive-development/pd-calendar

Version:

Webcomponent for calendar

495 lines (483 loc) 15.2 kB
import { css, html } from 'lit'; import { localized, msg } from '@lit/localize'; import { property, state } from 'lit/decorators.js'; import { parse, format } from 'date-fns'; import { PdBaseInputElement } from '@progressive-development/pd-forms'; import { pdIcons } from '@progressive-development/pd-icon'; import '@progressive-development/pd-forms/pd-input'; import '@progressive-development/pd-forms/pd-button'; import '@progressive-development/pd-forms/pd-select'; import '../pd-calendar.js'; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; const UNDEF = "UNDEF"; const INPUT_TYPE_DATE = 7; const DEFAULT_FORMAT = "yyyy-MM-dd"; const DATE_TIMES = [ { name: "Uhrzeit auswählen", value: UNDEF }, { name: "07:00", value: "07:00" }, { name: "07:30", value: "07:30" }, { name: "08:00", value: "08:00" }, { name: "08:30", value: "08:30" }, { name: "09:00", value: "09:00" }, { name: "09:30", value: "09:30" }, { name: "10:00", value: "10:00" }, { name: "10:30", value: "10:30" }, { name: "11:00", value: "11:00" }, { name: "11:30", value: "11:30" }, { name: "12:00", value: "12:00" }, { name: "12:30", value: "12:30" }, { name: "13:00", value: "13:00" }, { name: "13:30", value: "13:30" }, { name: "14:00", value: "14:00" }, { name: "14:30", value: "14:30" }, { name: "15:00", value: "15:00" }, { name: "15:30", value: "15:30" }, { name: "16:00", value: "16:00" }, { name: "16:30", value: "16:30" }, { name: "17:00", value: "17:00" }, { name: "17:30", value: "17:30" }, { name: "18:00", value: "18:00" }, { name: "18:30", value: "18:30" }, { name: "19:00", value: "19:00" }, { name: "19:30", value: "19:30" } ]; function getDateRangeValue(start, end, formatStr = DEFAULT_FORMAT) { return `${start ? format(start, formatStr) : "--"} - ${end ? format(end, formatStr) : "--"}`; } function getDateTimeValue(date, time, formatStr = DEFAULT_FORMAT) { return `${date ? format(date, formatStr) : ""} ${time && time !== UNDEF ? time : ""}`; } function getDataForDaysBetween(start, end) { const result = {}; let from = new Date(start); let to = new Date(end); if (from > to) [from, to] = [to, from]; while (from <= to) { result[format(from, DEFAULT_FORMAT)] = [{ special: true }]; from.setDate(from.getDate() + 1); } return result; } let PdDatepicker = class extends PdBaseInputElement { constructor() { super(); this.dateRange = false; this.hideToday = false; this.withYearPopup = []; this.withTime = false; this._showCalendar = false; this._timeSelection = UNDEF; this._calendarPosition = "bottom"; this._inputType = INPUT_TYPE_DATE; this._validators = [this._requiredDateTimeValidator.bind(this)]; } _requiredDateTimeValidator() { return this.required && !this._startDateValue || this.dateRange && this._startDateValue && !this._endDateValue || this.withTime && this._startDateValue && this._timeSelection === UNDEF ? msg("Eingabe unvollständig", { id: "pd.form.field.selectDateTimeRequired" }) : null; } update(changedProps) { if (changedProps.has("initialDate") && this.initialDate) { this._prepareFieldsFromInitDate(); this._updateInputField(false, void 0); } super.update(changedProps); } render() { return html` <pd-input id="datePickerId" .label="${this.label}" .placeHolder="${this.placeHolder}" .icon="${pdIcons.ICON_DATE}" .initValue="${this._value}" ?readonly=${true} ?required="${this.required}" ?disabled="${this.disabled}" icon-right @click="${this._inputClick}" @pd-form-element-change="${this._innerFormChange}" @pd-form-element-blur="${this._innerBlur}" @pd-form-element-focus="${this._innerFocus}" @pd-form-element-register="${this._innerRegister}" @enter-pressed="${this._innerEnter}" > <div slot="icon-right"> <slot name="icon-right"></slot> </div> ></pd-input > ${this._renderErrorMsg()} <div class="calendar-small ${this._calendarPosition}"> <pd-calendar .withYearPopup="${this.withYearPopup}" .refDate="${this._startDateValue}" .data="${this._rangeSelection || {}}" numberClass="center" selectableDates showSelection prevMonthConstraint="-1" nextMonthConstraint="-1" @select-date="${this._triggerCalendarDateSelected}" @mouse-enter-date="${this._triggerHoverCalculateRange}" ></pd-calendar> ${this.withTime ? html` <pd-select class="date-time-select" id="dateTimeSelectId" .values="${DATE_TIMES}" label="${msg("Uhrzeit", { id: "pd.datepicker.time" })}" initValue="${this._timeSelection}" required @pd-form-element-change="${(e) => { this._timeSelection = e.detail.value; e.stopPropagation(); }}" @pd-form-element-blur="${this._innerBlur}" @pd-form-element-focus="${this._innerFocus}" @pd-form-element-register="${this._innerRegister}" @enter-pressed="${this._innerEnter}" ></pd-select> ` : ""} <div class="bottom-buttons"> ${!this.hideToday ? html`<pd-button text="${msg("Heute", { id: "pd.datepicker.today" })}" @button-clicked="${this._triggerSetTodayButton}" ></pd-button>` : ""} <pd-button text="${msg("Reset", { id: "pd.datepicker.reset" })}" @button-clicked="${this._triggerResetButton}" ></pd-button> ${this.withTime ? html` <pd-button id="buttonSetValueId" text="Ok" ?disabled="${!this._startDateValue || this._timeSelection === UNDEF}" @button-clicked="${this._triggerSetValueButton}" ></pd-button> ` : ""} </div> </div> `; } /** * Getter for outside call. * * @returns obj: { start?: Date; end?: Date } - the prepared date values. */ get dateValues() { const start = this.withTime && this._startDateValue ? parse( `${format(this._startDateValue, "yyyy-MM-dd")} ${this._timeSelection}`, "yyyy-MM-dd HH:mm", /* @__PURE__ */ new Date() ) : this._startDateValue; return { start, end: this._endDateValue }; } reset() { if (this.initialDate) { this._prepareFieldsFromInitDate(); } else { this._clearFields(); } super.reset(); } clear() { this._clearFields(); this._updateInputField(true, void 0); } /** * @returns formated value from current field selections. */ _getInitialValue() { return this._formatDateValue(); } /** * @returns parsed date */ _getParsedValue() { return this.dateValues; } /** * Init _startDateValue and _timeSelection from initial date. */ _prepareFieldsFromInitDate() { if (this.initialDate) { this._startDateValue = new Date( this.initialDate.getFullYear(), this.initialDate.getMonth(), this.initialDate.getDate() ); if (this.withTime) { this._timeSelection = format(this.initialDate, "HH:mm"); } } } /** * Clear _startDateValue and _timeSelection. */ _clearFields() { this._startDateValue = void 0; this._endDateValue = void 0; this._rangeSelection = void 0; this._timeSelection = UNDEF; } /** * Update element value from input fields. */ _updateInputField(dispatchEvent, e) { this._handleChangedValue( this._formatDateValue(), e, dispatchEvent, false, void 0 ); } /** * Toogle view calendar on click into pd-input field. */ _inputClick() { const input = this.shadowRoot?.getElementById("datePickerId"); if (input) { const rect = input.getBoundingClientRect(); const spaceBelow = window.innerHeight - rect.bottom; const spaceAbove = rect.top; this._calendarPosition = spaceBelow < 300 && spaceAbove > spaceBelow ? "top" : "bottom"; this._showCalendar = !this._showCalendar; setTimeout(() => { this.shadowRoot?.querySelector(".calendar-small")?.scrollIntoView({ behavior: "smooth", block: "nearest" }); }, 0); } } /** * Triggered by date selection in small calendar. * Set internal values depending on daterange configuration. * * @param e Event from small calendar. */ _triggerCalendarDateSelected(e) { const date = e.detail.date; if (this.dateRange) { if (!this._startDateValue) { this._startDateValue = date; this._endDateValue = void 0; } else if (!this._endDateValue) { if (date.getTime() < this._startDateValue.getTime()) { this._endDateValue = this._startDateValue; this._startDateValue = date; } else { this._endDateValue = date; } this._showCalendar = false; } else { this._startDateValue = date; this._endDateValue = void 0; } } else { this._startDateValue = date; if (this.withTime) { const el = this.shadowRoot?.getElementById( this._timeSelection === UNDEF ? "dateTimeSelectId" : "buttonSetValueId" ); el?.focus(); } else { this._showCalendar = false; } } if (!this._showCalendar) { this._updateInputField(true, e); } e.stopPropagation(); } /** * Triggered by Today button in small calendar view. * Set calendar date to today. */ _triggerSetTodayButton(e) { this._startDateValue = /* @__PURE__ */ new Date(); if (this.dateRange) { this._endDateValue = void 0; this._rangeSelection = void 0; } else { this._showCalendar = this.withTime; } this._updateInputField(!this._showCalendar, e); e.stopPropagation(); } /** * Triggered by Reset button in small calendar view. * Reset selected values to origin state (initaldate or empty). */ _triggerResetButton(e) { this.reset(); this._showCalendar = false; this.shadowRoot?.getElementById("datePickerId")?.reset(); e.stopPropagation(); } /** * Triggered by ok button in small calendar view. * Set selected value and close calendar. */ _triggerSetValueButton(e) { this._updateInputField(true, e); this._showCalendar = false; e.stopPropagation(); } /** * Calculate range of dates between two dates on mouse over. * Only needed for date range visuell selection in small calendar. * * @param e */ _triggerHoverCalculateRange(e) { if (this._startDateValue && this.dateRange && e.detail.date && !this._endDateValue) { this._rangeSelection = getDataForDaysBetween( this._startDateValue, e.detail.date ); } } /** * Format date value from _startDateValue and _startDateValue and _timeSelection. * Depending on daterange and withtime configuration. * * @returns string with date value */ _formatDateValue() { if (!this._startDateValue && !this._endDateValue && this._timeSelection === UNDEF) return ""; return this.dateRange ? getDateRangeValue( this._startDateValue, this._endDateValue, this.dateFormat ) : getDateTimeValue( this._startDateValue, this._timeSelection, this.dateFormat ); } // ###### Start Inner Element Events _innerEnter(e) { e.stopPropagation(); } _innerRegister(e) { e.stopPropagation(); } _innerUnRegister(e) { e.stopPropagation(); } _innerFocus(e) { e.stopPropagation(); } _innerBlur(e) { e.stopPropagation(); } _innerFormChange(e) { e.stopPropagation(); } }; PdDatepicker.styles = [ PdBaseInputElement.styles, css` :host { position: relative; } :host([disabled]) { pointer-events: none; } :host([_showCalendar]) .calendar-small { visibility: visible; opacity: 1; } .calendar-small { position: absolute; background-color: white; visibility: hidden; z-index: 1000; box-shadow: var(--pd-menu-shadow, 3px 3px 5px grey); transition: 0.5s ease-out; transform: translateY(0); opacity: 0; padding: 0.5em 1em 0.5em 0.2em; --pd-calendar-width: var(--pd-input-field-width, 250px); --pd-calendar-cell-height: 30px; --pd-calendar-week-title-bg-col: white; --pd-calendar-week-title-font-col: #0a3a48; --pd-calendar-cell-selectable-shadow: 0; --pd-calendar-cell-day-free-bg-col: white; --pd-calendar-cell-day-info-free-col: #0a3a48; --pd-calendar-title-font-size: 20px; --pd-calendar-title-icon-size: 20px; --pd-calendar-weekday-title-font-size: 15px; --pd-calendar-number-font-size: 15px; } .calendar-small.bottom { top: 100%; /* Unter dem Input-Feld */ } .calendar-small.top { bottom: 100%; /* Über dem Input-Feld */ } .bottom-buttons { display: flex; --pd-button-width: 78px; } ` ]; __decorateClass([ property({ type: String }) ], PdDatepicker.prototype, "locale", 2); __decorateClass([ property({ type: Object }) ], PdDatepicker.prototype, "initialDate", 2); __decorateClass([ property({ type: String }) ], PdDatepicker.prototype, "dateFormat", 2); __decorateClass([ property({ type: Boolean }) ], PdDatepicker.prototype, "dateRange", 2); __decorateClass([ property({ type: Boolean }) ], PdDatepicker.prototype, "hideToday", 2); __decorateClass([ property({ type: Array }) ], PdDatepicker.prototype, "withYearPopup", 2); __decorateClass([ property({ type: String }) ], PdDatepicker.prototype, "placeHolderTime", 2); __decorateClass([ property({ type: Boolean }) ], PdDatepicker.prototype, "withTime", 2); __decorateClass([ property({ type: String }) ], PdDatepicker.prototype, "min", 2); __decorateClass([ property({ type: String }) ], PdDatepicker.prototype, "max", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], PdDatepicker.prototype, "_showCalendar", 2); __decorateClass([ state() ], PdDatepicker.prototype, "_startDateValue", 2); __decorateClass([ state() ], PdDatepicker.prototype, "_endDateValue", 2); __decorateClass([ state() ], PdDatepicker.prototype, "_rangeSelection", 2); __decorateClass([ state() ], PdDatepicker.prototype, "_timeSelection", 2); PdDatepicker = __decorateClass([ localized() ], PdDatepicker); export { PdDatepicker };