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
JavaScript
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}
=${this.navigatePrevious}
>
<igc-icon
aria-hidden="true"
name="arrow_prev"
collection="default"
></igc-icon>
</button>
<button
part=${parts}
aria-label=${this.nextButtonLabel}
=${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}
=${() => 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}
=${() => 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
=${this.changeValue}
=${this.activeDateChanged}
=${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"
=${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"
=${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