UNPKG

igniteui-webcomponents

Version:

Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.

528 lines (527 loc) 20.6 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var IgcCalendarComponent_1; import { html, nothing } from 'lit'; import { property, query, queryAll, state } from 'lit/decorators.js'; import { choose } from 'lit/directives/choose.js'; import { createRef, ref } from 'lit/directives/ref.js'; import { styleMap } from 'lit/directives/style-map.js'; import { themes } from '../../theming/theming-decorator.js'; import { addKeybindings, arrowDown, arrowLeft, arrowRight, arrowUp, endKey, homeKey, pageDownKey, pageUpKey, shiftKey, } from '../common/controllers/key-bindings.js'; import { watch } from '../common/decorators/watch.js'; import { registerComponent } from '../common/definitions/register.js'; import { IgcCalendarResourceStringEN } from '../common/i18n/calendar.resources.js'; import { createDateTimeFormatters } from '../common/localization/intl-formatters.js'; import { EventEmitterMixin } from '../common/mixins/event-emitter.js'; import { clamp, findElementFromEventPath, first, formatString, last, partNameMap, } from '../common/util.js'; import IgcIconComponent from '../icon/icon.js'; import { IgcCalendarBaseComponent } from './base.js'; import IgcDaysViewComponent from './days-view/days-view.js'; import { MONTHS_PER_ROW, YEARS_PER_PAGE, YEARS_PER_ROW, areSameMonth, getYearRange, isDateInRanges, } from './helpers.js'; import { CalendarDay } from './model.js'; import IgcMonthsViewComponent from './months-view/months-view.js'; import { styles } from './themes/calendar.base.css.js'; import { all } from './themes/calendar.js'; import IgcYearsViewComponent from './years-view/years-view.js'; export const focusActiveDate = Symbol(); let IgcCalendarComponent = IgcCalendarComponent_1 = class IgcCalendarComponent extends EventEmitterMixin(IgcCalendarBaseComponent) { static register() { registerComponent(IgcCalendarComponent_1, IgcIconComponent, IgcDaysViewComponent, IgcMonthsViewComponent, IgcYearsViewComponent); } get _isDayView() { return this.activeView === 'days'; } get _isMonthView() { return this.activeView === 'months'; } get _isYearView() { return this.activeView === 'years'; } get previousButtonLabel() { if (this._isDayView) { return this.resourceStrings.previousMonth; } if (this._isMonthView) { return this.resourceStrings.previousYear; } return formatString(this.resourceStrings.previousYears, YEARS_PER_PAGE); } get nextButtonLabel() { if (this._isDayView) { return this.resourceStrings.nextMonth; } if (this._isMonthView) { return this.resourceStrings.nextYear; } return formatString(this.resourceStrings.nextYears, YEARS_PER_PAGE); } localeChange() { this._intl.locale = this.locale; } formatOptionsChange() { this._intl.update({ month: { month: this.formatOptions.month, }, }); } async [focusActiveDate]() { await this.updateComplete; if (this._isDayView) { return this.daysViews.item(this.activeDaysViewIndex).focusActiveDate(); } if (this._isMonthView) { return this.monthsView.focusActiveDate(); } if (this._isYearView) { return this.yearsView.focusActiveDate(); } } updateViewIndex(date, delta) { if (this.visibleMonths === 1) return; const index = this.activeDaysViewIndex; const view = CalendarDay.from(this.daysViews.item(index).activeDate); if (date.month !== view.month) { this.activeDaysViewIndex = clamp(index + delta, 0, this.visibleMonths - 1); } } getSubsequentActiveDate(start, delta) { const disabled = this._disabledDates; let beginning = start.clone(); while (isDateInRanges(beginning, disabled)) { beginning = beginning.add('day', delta); } return beginning; } handleArrowKey(period, delta) { if (this._isDayView) { const date = this.getSubsequentActiveDate(this._activeDate.add(period, delta), delta); this.updateViewIndex(date, delta); this._activeDate = date; } else { const monthOrYear = this._isMonthView ? 'month' : 'year'; const monthOrYearDelta = (this._isMonthView ? MONTHS_PER_ROW : YEARS_PER_ROW) * delta; this._activeDate = this.getSubsequentActiveDate(this._activeDate.add(monthOrYear, period === 'week' ? monthOrYearDelta : delta), delta); } this[focusActiveDate](); } onPageKeys(delta) { const unit = this._isDayView ? 'month' : 'year'; const increment = (this._isYearView ? YEARS_PER_PAGE : 1) * delta; this._activeDate = this.getSubsequentActiveDate(this._activeDate.add(unit, increment), increment); this[focusActiveDate](); } onShiftPageKeys(delta) { if (this._isDayView) { this._activeDate = this.getSubsequentActiveDate(this._activeDate.add('year', delta), delta); this[focusActiveDate](); } } onHomeKey() { if (this._isDayView) { const first = CalendarDay.from(this.daysViews.item(0).activeDate); this._activeDate = this.getSubsequentActiveDate(first.set({ date: 1 }), 1); this.activeDaysViewIndex = 0; } if (this._isMonthView) { this._activeDate = this.getSubsequentActiveDate(this._activeDate.set({ month: 0 }), 1); } if (this._isYearView) { this._activeDate = this.getSubsequentActiveDate(this._activeDate.set({ year: getYearRange(this._activeDate, YEARS_PER_PAGE).start, }), 1); } this[focusActiveDate](); } onEndKey() { if (this._isDayView) { const index = this.daysViews.length - 1; const last = CalendarDay.from(this.daysViews.item(index).activeDate); this._activeDate = this.getSubsequentActiveDate(last.set({ month: last.month + 1, date: 0 }), -1); this.activeDaysViewIndex = index; } if (this._isMonthView) { this._activeDate = this.getSubsequentActiveDate(this._activeDate.set({ month: 11 }), -1); } if (this._isYearView) { this._activeDate = this.getSubsequentActiveDate(this._activeDate.set({ year: getYearRange(this._activeDate, YEARS_PER_PAGE).end, }), -1); } this[focusActiveDate](); } isNotFromCalendarView(_, event) { return !findElementFromEventPath(`${IgcDaysViewComponent.tagName}, ${IgcMonthsViewComponent.tagName}, ${IgcYearsViewComponent.tagName}`, event); } constructor() { super(); this.contentRef = createRef(); this.activeDaysViewIndex = 0; this.hideOutsideDays = false; this.hideHeader = false; this.headerOrientation = 'horizontal'; this.orientation = 'horizontal'; this.visibleMonths = 1; this.activeView = 'days'; this.formatOptions = { month: 'long', weekday: 'narrow' }; this.resourceStrings = IgcCalendarResourceStringEN; this._intl = createDateTimeFormatters(this.locale, { month: { month: this.formatOptions.month, }, monthLabel: { month: 'long' }, weekday: { weekday: 'short' }, monthDay: { month: 'short', day: 'numeric' }, yearLabel: { month: 'long', year: 'numeric' }, }); addKeybindings(this, { skip: this.isNotFromCalendarView, ref: this.contentRef, bindingDefaults: { preventDefault: true, triggers: ['keydownRepeat'] }, }) .set(arrowLeft, this.handleArrowKey.bind(this, 'day', -1)) .set(arrowRight, this.handleArrowKey.bind(this, 'day', 1)) .set(arrowUp, this.handleArrowKey.bind(this, 'week', -1)) .set(arrowDown, this.handleArrowKey.bind(this, 'week', 1)) .set([shiftKey, pageUpKey], this.onShiftPageKeys.bind(this, -1)) .set([shiftKey, pageDownKey], this.onShiftPageKeys.bind(this, 1)) .set(pageUpKey, this.onPageKeys.bind(this, -1)) .set(pageDownKey, this.onPageKeys.bind(this, 1)) .set(homeKey, this.onHomeKey) .set(endKey, this.onEndKey); } renderNavigationButtons() { const parts = partNameMap({ 'navigation-button': true, vertical: this.orientation === 'vertical', }); return html ` <div part="navigation-buttons"> <button part=${parts} aria-label=${this.previousButtonLabel} @click=${this.navigatePrevious} > <igc-icon aria-hidden="true" name="arrow_prev" collection="default" ></igc-icon> </button> <button part=${parts} aria-label=${this.nextButtonLabel} @click=${this.navigateNext} > <igc-icon aria-hidden="true" name="arrow_next" collection="default" ></igc-icon> </button> </div> `; } renderMonthButtonNavigation(active, viewIndex) { const labelFmt = this._intl.get('monthLabel').format; const valueFmt = this._intl.get('month').format; const ariaLabel = `${labelFmt(active.native)}, ${this.resourceStrings.selectMonth}`; return html ` <button part="months-navigation" aria-label=${ariaLabel} @click=${() => this.switchToMonths(viewIndex)} > ${valueFmt(active.native)} </button> `; } renderYearButtonNavigation(active, viewIndex) { const fmt = this._intl.get('yearLabel').format; const ariaLabel = `${active.year}, ${this.resourceStrings.selectYear}`; const ariaSkip = this._isDayView ? fmt(active.native) : active.year; return html ` <span class="aria-off-screen" aria-live="polite">${ariaSkip}</span> <button part="years-navigation" aria-label=${ariaLabel} @click=${() => this.switchToYears(viewIndex)} > ${active.year} </button> `; } renderYearRangeNavigation(active) { const { start, end } = getYearRange(active, YEARS_PER_PAGE); return html ` <span part="years-range" aria-live="polite"> ${start} - ${end} </span> `; } renderNavigation(date, showButtons = true, viewIndex = 0) { const activeDate = date ?? this._activeDate; return html ` <div part="navigation"> <div part="picker-dates"> ${this._isDayView ? this.renderMonthButtonNavigation(activeDate, viewIndex) : nothing} ${this._isDayView || this._isMonthView ? this.renderYearButtonNavigation(activeDate, viewIndex) : nothing} ${this._isYearView ? this.renderYearRangeNavigation(activeDate) : nothing} </div> ${showButtons ? this.renderNavigationButtons() : nothing} </div> `; } renderHeader() { if (this.hideHeader || this._isMultiple) { return nothing; } const title = this._isSingle ? this.resourceStrings.selectDate : this.resourceStrings.selectRange; return html ` <div part="header"> <h5 part="header-title"> <slot name="title">${title}</slot> </h5> <h2 part="header-date">${this.renderHeaderDate()}</h2> </div> `; } renderHeaderDateSingle() { const date = this.value ?? CalendarDay.today.native; const day = this._intl.get('weekday').format(date); const month = this._intl.get('monthDay').format(date); const separator = this.headerOrientation === 'vertical' ? html `<br />` : ' '; const formatted = html `${day},${separator}${month}`; return html `<slot name="header-date">${formatted}</slot>`; } renderHeaderDateRange() { const values = this.values; const fmt = this._intl.get('monthDay').format; const { startDate, endDate } = this.resourceStrings; const start = this._hasValues ? fmt(first(values)) : startDate; const end = this._hasValues && values.length > 1 ? fmt(last(values)) : endDate; return html ` <slot name="header-date"> <span>${start}</span> <span> - </span> <span>${end}</span> </slot> `; } renderHeaderDate() { return this._isSingle ? this.renderHeaderDateSingle() : this.renderHeaderDateRange(); } renderDaysView() { const activeDates = this.getActiveDates(); const horizontal = this.orientation === 'horizontal'; const length = activeDates.length - 1; const format = this.formatOptions .weekday; return html `${activeDates.map((date, idx) => html ` <div part="days-view-container"> ${this.renderNavigation(date, horizontal ? idx === length : idx === 0, idx)} <igc-days-view @igcChange=${this.changeValue} @igcActiveDateChange=${this.activeDateChanged} @igcRangePreviewDateChange=${this.rangePreviewDateChanged} part="days-view" exportparts="days-row, label, date-inner, week-number-inner, week-number, date, first, last, selected, inactive, hidden, current, weekend, range, special, disabled, single, preview" .active=${this.activeDaysViewIndex === idx} .activeDate=${date.native} .disabledDates=${this.disabledDates} .hideLeadingDays=${this.hideOutsideDays || idx !== 0} .hideTrailingDays=${this.hideOutsideDays || idx !== length} .locale=${this.locale} .rangePreviewDate=${this._rangePreviewDate?.native} .resourceStrings=${this.resourceStrings} .selection=${this.selection} .showWeekNumbers=${this.showWeekNumbers} .specialDates=${this._specialDates} .value=${this.value} .values=${this.values} .weekDayFormat=${format} .weekStart=${this.weekStart} ></igc-days-view> </div> `)}`; } renderMonthView() { const format = this.formatOptions .month; return html ` ${this.renderNavigation()} <igc-months-view part="months-view" exportparts="month, selected, month-inner, current" @igcChange=${this.changeMonth} .value=${this.activeDate} .locale=${this.locale} .monthFormat=${format} ></igc-months-view> `; } renderYearView() { return html ` ${this.renderNavigation()} <igc-years-view part="years-view" exportparts="year, selected, year-inner, current" @igcChange=${this.changeYear} .value=${this.activeDate} .yearsPerPage=${YEARS_PER_PAGE} ></igc-years-view> `; } render() { const direction = this._isDayView && this.orientation === 'horizontal'; const styles = { display: 'flex', flexGrow: 1, flexDirection: direction ? 'row' : 'column', }; return html ` ${this.renderHeader()} <div ${ref(this.contentRef)} part="content" style=${styleMap(styles)}> ${choose(this.activeView, [ ['days', () => this.renderDaysView()], ['months', () => this.renderMonthView()], ['years', () => this.renderYearView()], ])} </div> `; } changeMonth(event) { event.stopPropagation(); this.activeDate = event.detail; this.activeView = 'days'; this[focusActiveDate](); } changeYear(event) { event.stopPropagation(); this.activeDate = event.detail; this.activeView = 'months'; this[focusActiveDate](); } changeValue(event) { event.stopPropagation(); const view = event.target; if (this._isSingle) { this.value = view.value; } else { this.values = view.values; } this.emitEvent('igcChange', { detail: this._isSingle ? this.value : this.values, }); } activeDateChanged(event) { const view = event.target; const views = Array.from(this.daysViews); this.activeDaysViewIndex = views.indexOf(view); this.activeDate = event.detail; if (!areSameMonth(this.activeDate, view.activeDate)) { this[focusActiveDate](); } } rangePreviewDateChanged(event) { this._rangePreviewDate = event.detail ? CalendarDay.from(event.detail) : undefined; } getActiveDates() { const current = this.activeDaysViewIndex; const length = Math.max(this.visibleMonths, 1); return Array.from({ length }, (_, i) => this._activeDate.add('month', i - current)); } activateDaysView(index) { const view = this.daysViews.item(index); this.activeDate = view.activeDate; this.activeDaysViewIndex = index; } navigatePrevious() { const unit = this._isDayView ? 'month' : 'year'; const delta = this._isYearView ? YEARS_PER_PAGE : 1; this._activeDate = this._activeDate.add(unit, -delta); } navigateNext() { const unit = this._isDayView ? 'month' : 'year'; const delta = this._isYearView ? YEARS_PER_PAGE : 1; this._activeDate = this._activeDate.add(unit, delta); } switchToMonths(viewIndex) { this.activateDaysView(viewIndex); this.activeView = 'months'; this[focusActiveDate](); } switchToYears(viewIndex) { if (this._isDayView) { this.activateDaysView(viewIndex); } this.activeView = 'years'; this[focusActiveDate](); } }; IgcCalendarComponent.tagName = 'igc-calendar'; IgcCalendarComponent.styles = styles; __decorate([ state() ], IgcCalendarComponent.prototype, "activeDaysViewIndex", void 0); __decorate([ queryAll(IgcDaysViewComponent.tagName) ], IgcCalendarComponent.prototype, "daysViews", void 0); __decorate([ query(IgcMonthsViewComponent.tagName) ], IgcCalendarComponent.prototype, "monthsView", void 0); __decorate([ query(IgcYearsViewComponent.tagName) ], IgcCalendarComponent.prototype, "yearsView", void 0); __decorate([ property({ type: Boolean, attribute: 'hide-outside-days', reflect: true }) ], IgcCalendarComponent.prototype, "hideOutsideDays", void 0); __decorate([ property({ type: Boolean, attribute: 'hide-header', reflect: true }) ], IgcCalendarComponent.prototype, "hideHeader", void 0); __decorate([ property({ reflect: true, attribute: 'header-orientation' }) ], IgcCalendarComponent.prototype, "headerOrientation", void 0); __decorate([ property() ], IgcCalendarComponent.prototype, "orientation", void 0); __decorate([ property({ type: Number, attribute: 'visible-months' }) ], IgcCalendarComponent.prototype, "visibleMonths", void 0); __decorate([ property({ attribute: 'active-view' }) ], IgcCalendarComponent.prototype, "activeView", void 0); __decorate([ property({ attribute: false }) ], IgcCalendarComponent.prototype, "formatOptions", void 0); __decorate([ property({ attribute: false }) ], IgcCalendarComponent.prototype, "resourceStrings", void 0); __decorate([ watch('locale') ], IgcCalendarComponent.prototype, "localeChange", null); __decorate([ watch('formatOptions') ], IgcCalendarComponent.prototype, "formatOptionsChange", null); IgcCalendarComponent = IgcCalendarComponent_1 = __decorate([ themes(all) ], IgcCalendarComponent); export default IgcCalendarComponent; //# sourceMappingURL=calendar.js.map