import type { PropertyValues, TemplateResult } from 'lit' import { css, html } from 'lit' import { property, query, queryAll, state } from 'lit/decorators.js' import type { DirectiveClass, DirectiveResult } from 'lit/directive.js' import AirDatepicker from 'air-datepicker' import localeEn from 'air-datepicker/locale/en' import localeEs from 'air-datepicker/locale/es' import datepicker_css from 'air-datepicker/air-datepicker.css' import { classMap } from 'lit/directives/class-map.js' import type { Options } from 'simplebar' import SimpleBar from 'simplebar' import simpleBar_css from 'simplebar/dist/simplebar.css' import { map } from 'lit/directives/map.js' import { when } from 'lit/directives/when.js' import { endOfWeek, startOfWeek, subDays, subWeeks } from '@upswot/core' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import { dispatchCustomEvent } from '../../utils/dispatchCustomEvent' import { UsBaseElement } from '../base/UsBaseElement' import { getLocale, localizeManager, localized } from '../../dependencies' import { getWidgetViewClasses } from '../../utils/getWidgetViewClasses' import type { UsInput } from '../form/input/UsInput' import UsButton_vars from '../button/vars.pcss' import UsDatepicker_css from './datepicker.pcss' import vars_css from './vars.pcss' import type { IDatepickerData } from './models/IDatepickerData' import type { IDatepickerSelect } from './models/IDatepickerSelect' import type { IDatepickerRenderCell } from './models/IDatepickerRenderCell' import type { IDatepickerBaseOptions } from './models/IDatepickerBaseOptions' import { EnumDatepickerPresets } from './models/IDatepickerBaseOptions' import type { IDatepickerPreset } from './models/IDatepickerPreset' import '../form/input/UsInput' import '../menu/UsMenu' import '../button/UsButton' declare global { interface HTMLElementTagNameMap { 'us-datepicker': UsDatepicker } } @localized() @customElementIfNotExist('us-datepicker') export class UsDatepicker extends UsBaseElement implements IDatepickerBaseOptions { static override styles = [ UsBaseElement.styles, css([simpleBar_css] as TemplateStringsArray | any), css([vars_css] as TemplateStringsArray | any), css([datepicker_css] as TemplateStringsArray | any), css([UsDatepicker_css] as TemplateStringsArray | any), css([UsButton_vars] as TemplateStringsArray | any), ] @property() widgetView: IDatepickerBaseOptions['widgetView'] = 'vertical' @property({ type: String }) locale = getLocale() @property({ type: Object }) containerSize: IDatepickerBaseOptions['containerSize'] = null @property({ type: Object }) options: IDatepickerBaseOptions['options'] = {} @property({ type: Array }) defaultRange: IDatepickerBaseOptions['defaultRange'] = null @property({ type: Array }) datepickerData: IDatepickerBaseOptions['datepickerData'] = [] @property() initialPreset: EnumDatepickerPresets = EnumDatepickerPresets.CUSTOM @property({ type: Array }) availablePresets: EnumDatepickerPresets[] = [] @property({ type: Boolean }) withoutPresets: IDatepickerBaseOptions['withoutPresets'] = false @property({ type: Boolean }) disabled = false @property({ type: String }) placeholder = '' @property({ type: String }) inputLabel = '' @property({ type: String }) inputValidation = '' @property({ type: Boolean }) showLegend = false @property({ type: Object }) legend: IDatepickerBaseOptions['legend'] = undefined @property({ type: String, attribute: 'data-testid', reflect: true }) dataTestId = 'datepicker' @state() datepickerInstance: any = null @state() isDatepickerOpen = false @state() dateInInputValue = '' @state() isMenuOpen = false @state() filteredPresets: IDatepickerPreset[] = [] @state() currentPreset: IDatepickerPreset = { name: EnumDatepickerPresets.CUSTOM, label: 'Custom', } @state() datepickerOptions: IDatepickerBaseOptions['options'] = { inline: false, locale: localeEn, toggleSelected: true, range: true, dateFormat: 'd MMM yyyy', multipleDatesSeparator: ' - ', prevHtml: '', nextHtml: '', navTitles: { days: 'MMMM yyyy', }, offset: 0, autoClose: true, onSelect: this.onSelect.bind(this), onRenderCell: this.onRenderCell.bind(this), onShow: this.onShow.bind(this), onHide: this.onHide.bind(this), onChangeViewDate: this.onChangeViewDate.bind(this), onChangeView: this.onChangeView.bind(this), } @state() isHovered = false @query('.datepicker-anchor') datepickerAnchor!: HTMLInputElement @query('.datepicker') datepickerElement!: HTMLDivElement @query('.datepicker-container__over') datepickerContainerOver!: HTMLElement @query('.datepicker-activator') datepickerInput!: HTMLElement @queryAll('.datepicker__list-item') menuItems!: HTMLDivElement[] get datepickerDays() { if (this.datepickerInstance && this.datepickerInstance.$datepicker) return this.datepickerInstance.$datepicker.querySelector('.air-datepicker-body.-days-') return null } simpleBar!: SimpleBar referencePointDate = new Date() activeElementIndex = -1 presets = () => ([ { name: EnumDatepickerPresets.CUSTOM, label: localizeManager.datepicker_custom_preset(), }, { name: EnumDatepickerPresets.LAST_7_DAYS, label: localizeManager.datepicker_last_7_days_preset(), }, { name: EnumDatepickerPresets.LAST_30_DAYS, label: localizeManager.datepicker_last_28_days_preset(), }, { name: EnumDatepickerPresets.THIS_WEEK, label: localizeManager.datepicker_this_week_preset(), }, { name: EnumDatepickerPresets.LAST_WEEK, label: localizeManager.datepicker_last_week_preset(), }, { name: EnumDatepickerPresets.THIS_MONTH, label: localizeManager.datepicker_this_month_preset(), }, { name: EnumDatepickerPresets.LAST_MONTH, label: localizeManager.datepicker_last_month_preset(), }, { name: EnumDatepickerPresets.LAST_QUARTER, label: localizeManager.datepicker_last_quarter_preset(), }, { name: EnumDatepickerPresets.THIS_QUARTER, label: localizeManager.datepicker_this_quarter_preset(), }, { name: EnumDatepickerPresets.LAST_90_DAYS, label: localizeManager.datepicker_last_90_days_preset(), }, { name: EnumDatepickerPresets.LAST_3_MONTH, label: localizeManager.datepicker_last_3_month_preset(), }, { name: EnumDatepickerPresets.LAST_365_DAYS, label: localizeManager.datepicker_last_365_days_preset(), }, { name: EnumDatepickerPresets.NEXT_7_DAYS, label: localizeManager.datepicker_next_7_days_preset(), }, { name: EnumDatepickerPresets.NEXT_30_DAYS, label: localizeManager.datepicker_next_28_days_preset(), }, { name: EnumDatepickerPresets.NEXT_90_DAYS, label: localizeManager.datepicker_next_90_days_preset(), }, { name: EnumDatepickerPresets.NEXT_365_DAYS, label: localizeManager.datepicker_next_365_days_preset(), }, { name: EnumDatepickerPresets.ALL_TIME_PAST, label: localizeManager.datepicker_alltime_preset(), }, { name: EnumDatepickerPresets.ALL_TIME_FUTURE, label: localizeManager.datepicker_alltime_preset(), }, ]) onOuterClickBinded = this.onOuterClick.bind(this) handleDatepickerDayClickBinded = this.handleDatepickerDayClick.bind(this) connectedCallback() { super.connectedCallback() window.addEventListener('click', this.onOuterClickBinded, true) } disconnectedCallback() { window.removeEventListener('click', this.onOuterClickBinded, true) this.disposeSimpleBar() this.disposeCalendar() super.disconnectedCallback() } firstUpdated() { this.initDatepicker() } willUpdate(changedProperties: PropertyValues) { if (this.datepickerInstance === null) return const needUpdateOptions = this.needUpdateParams(changedProperties, 'options') const needUpdateDatepickerData = this.needUpdateParams(changedProperties, 'datepickerData') const needUpdateLocale = this.needUpdateParams(changedProperties, 'locale') if (needUpdateOptions || needUpdateDatepickerData || needUpdateLocale) this.updateDatepicker() if (changedProperties.has('defaultRange')) this.setDefaultDate() if (changedProperties.has('containerSize') && (this.isDatepickerOpen || this.datepickerOptions.inline)) { this.disposeSimpleBar() this.configurateDatepicker() } if (changedProperties.has('locale') || changedProperties.has('availablePresets')) this.filterPresets() } needUpdateParams(changedProperties: PropertyValues, key: keyof UsDatepicker) { if (key === 'locale') { const oldParams = changedProperties.get(key) const newParams = this.locale return changedProperties.has(key) && oldParams !== newParams } const oldParams = JSON.stringify(changedProperties.get(key)) const newParams = JSON.stringify(this.options) return changedProperties.has(key) && oldParams !== newParams } render(): TemplateResult { return html`
${when(this.withoutPresets, () => html` `, () => html`
    ${map(this.filteredPresets, preset => html`
  • ${preset.label}
  • `)}
`, )}
` } renderLegend() { const datepicker = this.datepickerContainerOver.querySelector('.air-datepicker') datepicker?.insertAdjacentHTML('beforeend', ``) } onOuterClick(event: Event) { if (!event.composedPath().includes(this) && this.datepickerOptions.inline) { const [dateFrom, dateTo]: [Date, Date] = this.datepickerInstance.selectedDates if (dateFrom && !dateTo) { this.datepickerInstance.selectedDates = [dateFrom, dateFrom] this.datepickerInstance.selectDate([dateFrom, dateFrom]) const formattedDate = this.getFormattedDates([dateFrom, dateFrom]) this.setInputValue(formattedDate[0]) this.handleDispatch() } } if (!event.composedPath().includes(this) && this.isDatepickerOpen) this.datepickerInstance.hide() } addHandleDatepickerDayClick() { if (this.datepickerDays) this.datepickerDays.addEventListener('click', this.handleDatepickerDayClickBinded) } removeHandleDatepickerDayClick() { if (this.datepickerDays) this.datepickerDays.removeEventListener('click', this.handleDatepickerDayClickBinded) } handleDatepickerDayClick() { if (this.datepickerOptions.inline && !this.withoutPresets) this.setCurrentPreset(EnumDatepickerPresets.CUSTOM) } getDatepickerClasses(): DirectiveResult { const widgetViewClasses = getWidgetViewClasses(this.widgetView) return classMap({ datepicker: true, ...widgetViewClasses, }) } getListItemClasses(preset: IDatepickerPreset): DirectiveResult { return classMap({ 'datepicker__list-item-outer': true, 'datepicker__list-item-outer_custom': preset.name === EnumDatepickerPresets.CUSTOM, 'datepicker__list-item-outer_active': preset.name === this.currentPreset.name, }) } mergeOptions() { this.setOptionsByLocale() this.datepickerOptions.container = this.datepickerContainerOver this.datepickerOptions = { ...this.datepickerOptions, ...this.options } } setReferencePointDate(preset: IDatepickerPreset) { if (preset.name.includes('last')) this.referencePointDate = this.datepickerOptions.maxDate ? new Date(this.datepickerOptions.maxDate) : new Date() if (preset.name.includes('next')) this.referencePointDate = this.datepickerOptions.minDate ? new Date(this.datepickerOptions.minDate) : new Date() } setOptionsByLocale() { switch (this.locale) { case 'es': this.datepickerOptions.dateFormat = 'd MMM yyyy' this.datepickerOptions.locale = localeEs break case 'en': default: this.datepickerOptions.dateFormat = 'MMM d, yyyy' this.datepickerOptions.locale = localeEn break } } initDatepicker() { try { this.mergeOptions() this.datepickerInstance = new AirDatepicker(this.datepickerAnchor, this.datepickerOptions) this.setDefaultDate() if (!this.withoutPresets) this.filterPresets() if (this.datepickerOptions.inline && !this.withoutPresets) this.addHandleDatepickerDayClick() document.getElementById('air-datepicker-global-container')?.remove() } catch (error) { console.error(error) } } getFormattedDates(dates: Date | Date[]) { const dateFormat = this.datepickerInstance.opts.dateFormat let formattedDates: string | string[] = '' if (Array.isArray(dates) && dates.length === 2) { formattedDates = [ this.datepickerInstance.formatDate(dates[0], dateFormat), this.datepickerInstance.formatDate(dates[1], dateFormat), ] } else { formattedDates = this.datepickerInstance.formatDate(dates, dateFormat) } return formattedDates } setDefaultDate() { if (this.defaultRange) { this.datepickerInstance.selectDate(this.defaultRange, { silent: true }) const formattedDates = this.getFormattedDates(this.defaultRange) this.setInputValue(formattedDates) } if (this.initialPreset && !this.withoutPresets) this.setDefaultPreset() } setDefaultPreset() { if (this.initialPreset !== EnumDatepickerPresets.CUSTOM) { const preset = this.presets().find(preset => preset.name === this.initialPreset)! this.selectPreset(preset, true) } else { this.setCurrentPreset(EnumDatepickerPresets.CUSTOM) } } selectPreset(preset: IDatepickerPreset, silent = false) { this.setReferencePointDate(preset) if (preset.name !== EnumDatepickerPresets.CUSTOM) this.datepickerInstance.clear() const presetHandlers = { [EnumDatepickerPresets.CUSTOM]: () => this.handleCustomClick(), [EnumDatepickerPresets.LAST_7_DAYS]: () => this.selectDateDaysAgo(7, silent), [EnumDatepickerPresets.THIS_WEEK]: () => this.selectDateThisWeek(silent), [EnumDatepickerPresets.LAST_WEEK]: () => this.selectDateWeeksAgo(1, silent), [EnumDatepickerPresets.THIS_MONTH]: () => this.selectDateThisMonth(silent), [EnumDatepickerPresets.LAST_MONTH]: () => this.selectDateMonthsAgo(1, silent), [EnumDatepickerPresets.LAST_3_MONTH]: () => this.selectDateMonthsAgo(3, silent), [EnumDatepickerPresets.NEXT_7_DAYS]: () => this.selectDateDaysAhead(7, silent), [EnumDatepickerPresets.LAST_30_DAYS]: () => this.selectDateDaysAgo(30, silent), [EnumDatepickerPresets.LAST_QUARTER]: () => this.selectDateThisQuarter(true, silent), [EnumDatepickerPresets.THIS_QUARTER]: () => this.selectDateThisQuarter(silent), [EnumDatepickerPresets.NEXT_30_DAYS]: () => this.selectDateDaysAhead(30, silent), [EnumDatepickerPresets.LAST_90_DAYS]: () => this.selectDateDaysAgo(90, silent), [EnumDatepickerPresets.NEXT_90_DAYS]: () => this.selectDateDaysAhead(90, silent), [EnumDatepickerPresets.NEXT_365_DAYS]: () => this.selectDateYearAhead(silent), [EnumDatepickerPresets.LAST_365_DAYS]: () => this.selectDateYearAgo(silent), [EnumDatepickerPresets.ALL_TIME_PAST]: () => this.selectDateLifetime(true, silent), [EnumDatepickerPresets.ALL_TIME_FUTURE]: () => this.selectDateLifetime(false, silent), } presetHandlers[preset.name]() this.setCurrentPreset(preset.name) if (preset.name !== EnumDatepickerPresets.CUSTOM) { this.closeMenu() if (!silent) this.handleDispatch() } } setInputValue(formattedDate: string | string[]) { if (!formattedDate || !formattedDate.length) { this.dateInInputValue = '' return } if (Array.isArray(formattedDate)) { if (formattedDate.length === 2 && formattedDate[0] === formattedDate[1]) { this.dateInInputValue = formattedDate[0] } else { this.dateInInputValue = formattedDate.length === 1 ? `${formattedDate[0]}` : `${formattedDate[0]} - ${formattedDate[1]}` } } else if (typeof formattedDate === 'string') { this.dateInInputValue = formattedDate } } initSimpleBar(simpleBarEl: HTMLElement) { if (simpleBarEl) this.simpleBar = new SimpleBar(simpleBarEl, { ariaLabel: 'simplebar-datepicker' } as Options) } disposeSimpleBar() { this.simpleBar && this.simpleBar.unMount() } getDefaultHeight() { let height = 0 if (this.datepickerInstance.$nav && this.datepickerInstance.$content) { const datepickerStyle = this.datepickerInstance.$datepicker.currentStyle || window.getComputedStyle(this.datepickerInstance.$datepicker) let navHeight = this.datepickerInstance.$nav.offsetHeight if (this.datepickerInstance.$datepicker.hasAttribute('data-simplebar')) { const $nav = this.datepickerInstance.$nav.querySelector('.air-datepicker-nav') const navStyle = $nav.currentStyle || window.getComputedStyle($nav) navHeight = navHeight + parseFloat(navStyle.marginBottom) } height = parseFloat(datepickerStyle.paddingTop) + parseFloat(datepickerStyle.paddingBottom) + navHeight + this.datepickerInstance.$content.offsetHeight } return height || this.datepickerInstance.$datepicker.offsetHeight } configurateDatepicker() { if (!this.containerSize || !this.datepickerInstance) return if (this.containerSize.height !== 0 && this.datepickerInstance.$datepicker) { const height = this.getDefaultHeight() if (height > this.containerSize.height) { this.datepickerInstance.$datepicker.style.height = `${this.containerSize.height}px` this.initSimpleBar(this.datepickerInstance.$datepicker) } else { this.datepickerInstance.$datepicker.style.height = `${height}px` } this.datepickerInstance.$datepicker.style.width = `${this.containerSize.width}px` } } async onChangeViewDate() { this.disposeSimpleBar() await this.updateComplete this.configurateDatepicker() } async onChangeView() { this.disposeSimpleBar() await this.updateComplete this.configurateDatepicker() } async onShow(isFinished: boolean) { if (!isFinished) this.configurateDatepicker() if (!isFinished && this.showLegend) this.renderLegend() this.isDatepickerOpen = true } onHide(isFinished: boolean) { if (!isFinished && this.containerSize) this.disposeSimpleBar() if (this.isDatepickerOpen) this.handleDispatch() this.isDatepickerOpen = false } onSelect({ formattedDate }: IDatepickerSelect): void { this.setInputValue(formattedDate) const shouldHandleDispatch = () => { if (this.datepickerOptions.range && formattedDate.length === 2) this.handleDispatch() else if (!this.datepickerOptions.range) this.handleDispatch() } if (this.withoutPresets) { shouldHandleDispatch() } else { this.dateInInputValue = (this.currentPreset.name === EnumDatepickerPresets.CUSTOM) ? this.dateInInputValue : this.currentPreset.label if (this.datepickerOptions.inline) shouldHandleDispatch() } } onRenderCell({ date, cellType, datepicker }: IDatepickerRenderCell) { let cellContent const { isOtherMonth, isOtherDecade, opts } = datepicker as any switch (cellType) { case 'day': cellContent = (!opts.showOtherMonths && isOtherMonth(date)) ? '' : date.getDate() break case 'month': cellContent = opts.locale[opts.monthsField][date.getMonth()] break case 'year': cellContent = (!opts.showOtherYears && isOtherDecade(date)) ? '' : date.getFullYear() } const cellHTML = `
${cellContent}
` if (this.checkDateInData(date)) { const itemData = this.datepickerData.find(item => new Date(item.date).toDateString() === date.toDateString()) const cellClass = (itemData && itemData.paymentTypes.length) ? itemData.paymentTypes.slice().sort().join('-') : '' return { html: cellHTML, classes: cellClass, disabled: false, } } return { html: cellHTML, classes: '', disabled: false, } } toggleMenu() { if (this.disabled) return this.isMenuOpen = !this.isMenuOpen } closeMenu() { this.isMenuOpen = false this.activeElementIndex = -1 } focusInput() { this.datepickerInput.focus() } focusMenuItem() { this.menuItems[this.activeElementIndex].focus() } toggleDatepicker() { if (this.isDatepickerOpen) { this.datepickerInstance.hide() this.datepickerInput.blur() } else { this.datepickerInstance.show() } } handleCustomClick() { if (this.withoutPresets) { this.toggleDatepicker() } else { this.closeMenu() this.datepickerAnchor.focus() } this.handleDatepickerView() } selectDateThisMonth(silent = false) { const currentDate = new Date(this.referencePointDate) const startOfMonth = currentDate.setDate(1) this.datepickerInstance.selectDate([startOfMonth, this.referencePointDate], { silent }) } selectDateThisQuarter(lastQuarter = false, silent = false) { const currentDate = new Date(this.referencePointDate) const currentMonth = currentDate.getMonth() const currentYear = currentDate.getFullYear() let startMonth = Math.floor(currentMonth / 3) * 3 if (lastQuarter) { startMonth -= 3 if (startMonth < 0) { startMonth += 12 currentDate.setFullYear(currentYear - 1) } } const startOfQuarter = new Date(currentDate.getFullYear(), startMonth, 1) const endMonth = startMonth + 2 const endOfQuarter = new Date(currentDate.getFullYear(), endMonth + 1, 0) this.datepickerInstance.selectDate([startOfQuarter, endOfQuarter], { silent }) } selectDateThisWeek(silent = false) { const currentDate = new Date(this.referencePointDate) const currentDay = currentDate.getDay() const diff = currentDate.getDate() - currentDay + (currentDay === 0 ? -6 : 1) const startOfWeek = new Date(currentDate.setDate(diff)) this.datepickerInstance.selectDate([startOfWeek, this.referencePointDate], { silent }) } selectDateWeeksAgo(weeks: number, silent = false) { const currentDate = new Date(this.referencePointDate) const targetDate = subWeeks(currentDate, weeks) const startOfWeekDate = startOfWeek(targetDate, { weekStartsOn: 1 }) const endOfWeekDate = endOfWeek(targetDate, { weekStartsOn: 1 }) this.datepickerInstance.selectDate([startOfWeekDate, endOfWeekDate], { silent }) } selectDateMonthsAgo(months: number, silent = false) { const startOfMonth = new Date(this.referencePointDate) startOfMonth.setMonth(startOfMonth.getMonth() - months) startOfMonth.setDate(1) const endOfMonth = new Date(this.referencePointDate.getFullYear(), this.referencePointDate.getMonth(), 0) this.datepickerInstance.selectDate([startOfMonth, endOfMonth], { silent }) } selectDateDaysAgo(days: number, silent = false) { const daysAgo = subDays(this.referencePointDate, days - 1) this.datepickerInstance.selectDate([daysAgo, this.referencePointDate], { silent }) } selectDateDaysAhead(days: number, silent = false) { const daysAhead = new Date(this.referencePointDate.getFullYear(), this.referencePointDate.getMonth(), this.referencePointDate.getDate() + (days - 1)) this.datepickerInstance.selectDate([daysAhead, this.referencePointDate], { silent }) } selectDateYearAgo(silent = false) { const yearAgo = new Date(this.referencePointDate.getFullYear() - 1, this.referencePointDate.getMonth(), this.referencePointDate.getDate()) this.datepickerInstance.selectDate([yearAgo, this.referencePointDate], { silent }) } selectDateYearAhead(silent = false) { const yearAhead = new Date(this.referencePointDate.getFullYear() + 1, this.referencePointDate.getMonth(), this.referencePointDate.getDate()) this.datepickerInstance.selectDate([yearAhead, this.referencePointDate], { silent }) } selectDateLifetime(isPast = false, silent = false) { if (isPast) { const minDate = new Date(new Date().setFullYear(new Date().getFullYear() - 5)) this.datepickerInstance.selectDate([minDate, this.referencePointDate], { silent }) } else { const minDate = this.referencePointDate this.datepickerInstance.selectDate([minDate, new Date(new Date().setFullYear(new Date().getFullYear() + 5))], { silent }) } } setCurrentPreset(presetName: EnumDatepickerPresets) { this.currentPreset = this.presets().find(preset => preset.name === presetName)! } filterPresets() { if (this.availablePresets.length) { this.filteredPresets = this.presets() .filter(preset => this.availablePresets.includes(preset.name)) .sort(this.sortPresetsCallback.bind(this)) } else { this.filteredPresets = this.presets() } } sortPresetsCallback(a, b) { const indexA = this.availablePresets.indexOf(a.name) const indexB = this.availablePresets.indexOf(b.name) return indexA - indexB } clickItemAction(preset: IDatepickerPreset) { this.selectPreset(preset) } keydownItemAction(event: KeyboardEvent, preset: IDatepickerPreset) { if (event.code === 'Enter') { this.selectPreset(preset) if (this.currentPreset.name !== EnumDatepickerPresets.CUSTOM) this.focusInput() } } async keydownInputAction(event: KeyboardEvent) { if (event.code !== 'Enter') return if (this.withoutPresets) { this.toggleDatepicker() await this.updateComplete if (this.isDatepickerOpen) { this.datepickerAnchor.focus() } else { this.datepickerAnchor.blur() this.datepickerInput.blur() } this.handleDatepickerView() } else { this.toggleMenu() } } handleHover() { this.isHovered = !this.isHovered } handleBlur(event: FocusEvent) { if (!this.withoutPresets) { const nextElement = event.relatedTarget as HTMLElement if (nextElement && !this.datepickerElement.contains(nextElement)) this.closeMenu() } } handleDatepickerKeydown(event: KeyboardEvent) { event.stopPropagation() if (event.key === 'Escape') { this.closeMenu() this.focusInput() } if (!this.withoutPresets) { if (event.key === 'ArrowUp') { event.preventDefault() this.activeElementIndex = this.activeElementIndex <= 0 ? this.menuItems.length - 1 : this.activeElementIndex - 1 this.focusMenuItem() } if (event.key === 'ArrowDown') { event.preventDefault() this.activeElementIndex = this.activeElementIndex === this.menuItems.length - 1 ? 0 : this.activeElementIndex + 1 this.focusMenuItem() } } } isDatepickerActive() { return this.isMenuOpen || this.isDatepickerOpen } checkDateInData(date: Date): boolean | IDatepickerData { if (this.datepickerData) { const data = this.datepickerData.find((item) => { return new Date(item.date).toDateString() === date.toDateString() }) return data || false } return false } handleDispatch() { const [dateFrom, dateTo]: [Date, Date] = this.datepickerInstance.selectedDates let date: Date[] = [] if (!dateFrom && !dateTo) return if (dateFrom && dateTo) date = [dateFrom, dateTo] if (dateFrom && !dateTo) date = [dateFrom, dateFrom] dispatchCustomEvent('changeDates', { value: this.dateInInputValue, date, preset: this.currentPreset }, this, false, false) this.validate() this.handleDatepickerView() } handleDatepickerView() { const size = { width: this.datepickerInstance.$datepicker.offsetWidth, height: this.datepickerInstance.$datepicker.offsetHeight, } dispatchCustomEvent('handleView', { size, isOpen: this.isDatepickerOpen }, this, false, false) } updateDatepicker() { if (this.datepickerInstance) { const hasInlineState = 'inline' in this.datepickerOptions const hasInlineProps = 'inline' in this.options const notEqual = this.datepickerOptions.inline !== this.options.inline if (hasInlineState && hasInlineProps && notEqual) { this.disposeCalendar() this.initDatepicker() return } this.mergeOptions() this.datepickerInstance.update(this.datepickerOptions) if (this.initialPreset && !this.withoutPresets) this.setDefaultPreset() } } disposeCalendar() { this.removeHandleDatepickerDayClick() this.disposeSimpleBar() this.datepickerInstance && this.datepickerInstance.destroy() } async validate() { if (this.inputValidation.length) { await this.updateComplete const input = this.datepickerInput as UsInput input.validate && input.validate() } } }