@progress/kendo-angular-dateinputs
Version:
Kendo UI for Angular Date Inputs Package - Everything you need to add date selection functionality to apps (DatePicker, TimePicker, DateInput, DateRangePicker, DateTimePicker, Calendar, and MultiViewCalendar).
485 lines (482 loc) • 22.3 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ElementRef, EventEmitter, HostBinding, Input, Output, Renderer2, TemplateRef, ViewChild } from '@angular/core';
import { cloneDate } from '@progress/kendo-date-math';
import { IntlService } from '@progress/kendo-angular-intl';
import { VirtualizationComponent } from '../virtualization/virtualization.component';
import { BusViewService } from './services/bus-view.service';
import { CalendarDOMService } from './services/dom.service';
import { CalendarViewEnum } from './models/view.enum';
import { MIN_DATE, MAX_DATE } from '../defaults';
import { dateInRange, hasChange, shiftWeekNames } from '../util';
import { HeaderComponent } from './header.component';
import { FooterComponent } from './footer.component';
import { ViewComponent } from './view.component';
import { KForOf } from './for.directive';
import { NgIf, NgFor } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "./services/bus-view.service";
import * as i2 from "@progress/kendo-angular-intl";
import * as i3 from "./services/dom.service";
const VIEWS_COUNT = 5;
const isEqualMonthYear = (date1, date2) => (date1 && date2 &&
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth());
/**
* @hidden
*/
export class ViewListComponent {
bus;
cdr;
intl;
dom;
renderer;
allowReverse;
cellTemplateRef;
weekNumberTemplateRef;
headerTitleTemplateRef;
headerTemplateRef;
footerTemplateRef;
showOtherMonthDays;
activeView;
cellUID;
focusedDate;
isActive = true;
min = new Date(MIN_DATE);
max = new Date(MAX_DATE);
selectedDates = [];
tabIndex = 0;
disabled = false;
id;
showFooter = false;
weekDaysFormat = 'short';
activeRangeEnd;
selectionRange;
size;
get weekNumber() {
return this.showWeekNumbers && this.isMonthView();
}
set weekNumber(showWeekNumbers) {
this.showWeekNumbers = showWeekNumbers;
}
cellEnter = new EventEmitter();
cellClick = new EventEmitter();
weekNumberCellClick = new EventEmitter();
activeDateChange = new EventEmitter();
todayButtonClick = new EventEmitter();
pageChange = new EventEmitter();
focusCalendar = new EventEmitter();
blurCalendar = new EventEmitter();
focusedCellChange = new EventEmitter();
virtualization;
headerComponent;
get headerTitle() {
return this.headerComponent?.title;
}
list;
getComponentClass = true;
get getComponentMonthClass() {
return this.activeView === CalendarViewEnum.month;
}
get getComponentYearClass() {
return this.activeView === CalendarViewEnum.year;
}
get getComponentDecadeClass() {
return this.activeView === CalendarViewEnum.decade;
}
get getComponentCenturyClass() {
return this.activeView === CalendarViewEnum.century;
}
get activeViewValue() {
return CalendarViewEnum[this.activeView];
}
service;
activeDate;
dates = [];
cols = [];
weekNames = [];
wideWeekNames = [];
take = VIEWS_COUNT;
skip;
total;
bottomOffset;
viewHeight;
viewOffset;
animateToIndex = true;
indexToScroll = -1;
showWeekNumbers;
minViewsToRender = 1;
intlSubscription;
constructor(bus, cdr, intl, dom, renderer) {
this.bus = bus;
this.cdr = cdr;
this.intl = intl;
this.dom = dom;
this.renderer = renderer;
}
ngOnInit() {
this.weekNames = this.getWeekNames(this.weekDaysFormat);
this.wideWeekNames = this.getWeekNames('wide');
this.bottomOffset = this.getBottomOffset();
this.viewOffset = -1 * this.dom.headerHeight;
this.viewHeight = this.dom.viewHeight(this.activeView);
this.intlSubscription = this.intl.changes.subscribe(this.intlChange.bind(this));
}
ngOnChanges(changes) {
this.service = this.bus.service(this.activeView);
if (!this.service) {
return;
}
this.cols = new Array(this.service.rowLength({ prependCell: this.weekNumber })).fill('');
this.weekNames = hasChange(changes, 'weekNumber') && this.weekNumber ? this.getWeekNames(this.weekDaysFormat) : this.weekNames;
if (hasChange(changes, 'weekDaysFormat') && !hasChange(changes, 'weekNumber')) {
this.weekNames = this.getWeekNames(this.weekDaysFormat);
}
this.wideWeekNames = hasChange(changes, 'weekNumber') && this.weekNumber ? this.getWeekNames('wide') : this.weekNames;
const activeViewChanged = hasChange(changes, 'activeView');
const focusedDate = this.focusedDate;
const viewDate = dateInRange(this.service.viewDate(focusedDate, this.max, this.minViewsToRender), this.min, this.max);
const total = this.service.total(this.min, this.max);
const totalChanged = this.total && this.total !== total;
const generateDates = totalChanged || !this.service.isInArray(focusedDate, this.dates);
this.skip = this.service.skip(viewDate, this.min);
this.total = total;
this.animateToIndex = !activeViewChanged;
this.bottomOffset = this.getBottomOffset();
this.viewHeight = this.dom.viewHeight(this.activeView);
if (generateDates) {
this.dates = this.service.datesList(viewDate, this.getTake(this.skip));
}
if (!isEqualMonthYear(this.activeDate, focusedDate)) {
this.activeDate = cloneDate(focusedDate);
}
const updateIndex = hasChange(changes, 'focusedDate') || activeViewChanged;
if (generateDates || updateIndex || this.virtualization.isIndexVisible(this.skip)) {
this.indexToScroll = this.service.skip(focusedDate, this.min);
}
}
ngOnDestroy() {
if (this.intlSubscription) {
this.intlSubscription.unsubscribe();
}
}
ngAfterViewInit() {
if (this.indexToScroll === -1) {
return;
}
this.virtualization.scrollToIndex(this.indexToScroll);
this.indexToScroll = -1;
}
ngAfterViewChecked() {
if (this.indexToScroll === -1) {
return;
}
this.virtualization[this.animateToIndex ? 'animateToIndex' : 'scrollToIndex'](this.indexToScroll);
this.animateToIndex = true;
this.indexToScroll = -1;
}
onPageChange({ skip }) {
this.dates = this.service.datesList(this.service.addToDate(this.min, skip), this.getTake(skip));
this.pageChange.emit();
}
scrollChange({ offset }) {
const el = this.list.nativeElement;
const translate = `translateY(${offset}px)`;
this.renderer.setStyle(el, 'transform', translate);
this.renderer.setStyle(el, '-ms-transform', translate);
}
setActiveDate(index) {
const candidate = this.service.addToDate(this.min, index);
this.activeDate = candidate;
this.activeDateChange.emit(candidate);
this.cdr.detectChanges();
}
isMonthView() {
return this.activeView === CalendarViewEnum.month;
}
isScrolled() {
return this.virtualization.isListScrolled(this.service.skip(this.focusedDate, this.min));
}
getTabIndex() {
return this.disabled ? null : this.tabIndex;
}
getBottomOffset() {
return this.getScrollableHeight() - this.dom.viewHeight(this.activeView);
}
getScrollableHeight() {
return this.activeView === CalendarViewEnum.month ?
this.dom.scrollableContentHeight :
this.dom.scrollableYearContentHeight;
}
getTake(skip) {
return Math.min(this.total - skip, this.take);
}
getWeekNames(nameType) {
const weekNames = shiftWeekNames(this.intl.dateFormatNames({ nameType: nameType, type: 'days' }), this.intl.firstDay());
return this.weekNumber ? [''].concat(weekNames) : weekNames;
}
intlChange() {
this.weekNames = this.getWeekNames(this.weekDaysFormat);
this.wideWeekNames = this.getWeekNames('wide');
if (this.isMonthView()) {
this.cdr.markForCheck();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewListComponent, deps: [{ token: i1.BusViewService }, { token: i0.ChangeDetectorRef }, { token: i2.IntlService }, { token: i3.CalendarDOMService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ViewListComponent, isStandalone: true, selector: "kendo-calendar-viewlist", inputs: { allowReverse: "allowReverse", cellTemplateRef: "cellTemplateRef", weekNumberTemplateRef: "weekNumberTemplateRef", headerTitleTemplateRef: "headerTitleTemplateRef", headerTemplateRef: "headerTemplateRef", footerTemplateRef: "footerTemplateRef", showOtherMonthDays: "showOtherMonthDays", activeView: "activeView", cellUID: "cellUID", focusedDate: "focusedDate", isActive: "isActive", min: "min", max: "max", selectedDates: "selectedDates", tabIndex: "tabIndex", disabled: "disabled", id: "id", showFooter: "showFooter", weekDaysFormat: "weekDaysFormat", activeRangeEnd: "activeRangeEnd", selectionRange: "selectionRange", size: "size", weekNumber: "weekNumber" }, outputs: { cellEnter: "cellEnter", cellClick: "cellClick", weekNumberCellClick: "weekNumberCellClick", activeDateChange: "activeDateChange", todayButtonClick: "todayButtonClick", pageChange: "pageChange", focusCalendar: "focusCalendar", blurCalendar: "blurCalendar", focusedCellChange: "focusedCellChange" }, host: { properties: { "class.k-vstack": "this.getComponentClass", "class.k-calendar-view": "this.getComponentClass", "class.k-calendar-monthview": "this.getComponentMonthClass", "class.k-calendar-yearview": "this.getComponentYearClass", "class.k-calendar-decadeview": "this.getComponentDecadeClass", "class.k-calendar-centuryview": "this.getComponentCenturyClass" } }, viewQueries: [{ propertyName: "virtualization", first: true, predicate: VirtualizationComponent, descendants: true }, { propertyName: "headerComponent", first: true, predicate: HeaderComponent, descendants: true }, { propertyName: "list", first: true, predicate: ["list"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
<kendo-calendar-header
[currentDate]="activeDate"
[size]="size"
[min]="min"
[max]="max"
[id]="id"
[activeView]="activeView"
[titleTemplateRef]="headerTitleTemplateRef"
[headerTemplateRef]="headerTemplateRef"
(todayButtonClick)="todayButtonClick.emit($event)"
>
</kendo-calendar-header>
<table class="k-calendar-weekdays k-calendar-table" [style.table-layout]="'auto'" *ngIf="isMonthView()">
<thead class="k-calendar-thead">
<tr class="k-calendar-tr">
<th class="k-calendar-th" *ngFor="let name of weekNames; let i = index;" scope="col" [attr.aria-label]="wideWeekNames[i]" role="columnheader">{{name}}</th>
</tr>
</thead>
</table>
<kendo-virtualization
[tabindex]="-1"
[skip]="skip"
[take]="take"
[total]="total"
[itemHeight]="viewHeight"
[topOffset]="viewOffset"
[bottomOffset]="bottomOffset"
[scrollOffsetSize]="viewOffset"
[maxScrollDifference]="viewHeight"
(pageChange)="onPageChange($event)"
(scrollChange)="scrollChange($event)"
(activeIndexChange)="setActiveDate($event)"
>
<table
#list
role="grid"
class="k-calendar-table"
[attr.tabindex]="getTabIndex()"
[attr.aria-labelledby]="id"
(focus)="focusCalendar.emit()"
(blur)="blurCalendar.emit($event)"
>
<colgroup><col *ngFor="let _ of cols" /></colgroup>
<tbody class="k-calendar-tbody"
*kFor="let date of dates"
kendoCalendarView
[allowReverse]="allowReverse"
[activeRangeEnd]="activeRangeEnd"
[selectionRange]="selectionRange"
[showOtherMonthDays]="showOtherMonthDays"
[headerTitle]="headerTitle"
role="rowgroup"
[activeView]="activeView"
[isActive]="isActive"
[min]="min" [max]="max"
[cellUID]="cellUID"
[focusedDate]="focusedDate"
[selectedDates]="selectedDates"
[weekNumber]="weekNumber"
[templateRef]="cellTemplateRef"
[weekNumberTemplateRef]="weekNumberTemplateRef"
[viewDate]="date"
(cellClick)="cellClick.emit($event)"
(weekNumberCellClick)="weekNumberCellClick.emit($event)"
(focusedCellId)="focusedCellChange.emit($event)"
(cellEnter)="cellEnter.emit($event)"
></tbody>
</table>
</kendo-virtualization>
<kendo-calendar-footer
*ngIf="showFooter"
[footerTemplateRef]="footerTemplateRef"
[activeViewValue]="activeViewValue"
[currentDate]="activeDate">
</kendo-calendar-footer>
`, isInline: true, dependencies: [{ kind: "component", type: HeaderComponent, selector: "kendo-calendar-header", inputs: ["activeView", "currentDate", "min", "max", "rangeLength", "titleTemplateRef", "headerTemplateRef", "isPrevDisabled", "isNextDisabled", "showNavigationButtons", "orientation", "id", "size"], outputs: ["todayButtonClick", "prevButtonClick", "nextButtonClick"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: VirtualizationComponent, selector: "kendo-virtualization", inputs: ["direction", "itemHeight", "itemWidth", "topOffset", "bottomOffset", "maxScrollDifference", "scrollOffsetSize", "scrollDuration", "skip", "take", "total"], outputs: ["activeIndexChange", "pageChange", "scrollChange"] }, { kind: "directive", type: KForOf, selector: "[kFor][kForOf]", inputs: ["kForOf", "kForTrackBy", "kForTemplate"] }, { kind: "component", type: ViewComponent, selector: "[kendoCalendarView]", inputs: ["allowReverse", "showOtherMonthDays", "direction", "isActive", "activeView", "cellUID", "focusedDate", "viewDate", "activeRangeEnd", "selectionRange", "min", "max", "selectedDates", "weekNumber", "viewIndex", "templateRef", "weekNumberTemplateRef", "headerTitle"], outputs: ["cellClick", "weekNumberCellClick", "cellEnter", "cellLeave", "focusedCellId"] }, { kind: "component", type: FooterComponent, selector: "kendo-calendar-footer", inputs: ["footerTemplateRef", "activeViewValue", "currentDate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewListComponent, decorators: [{
type: Component,
args: [{
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'kendo-calendar-viewlist',
template: `
<kendo-calendar-header
[currentDate]="activeDate"
[size]="size"
[min]="min"
[max]="max"
[id]="id"
[activeView]="activeView"
[titleTemplateRef]="headerTitleTemplateRef"
[headerTemplateRef]="headerTemplateRef"
(todayButtonClick)="todayButtonClick.emit($event)"
>
</kendo-calendar-header>
<table class="k-calendar-weekdays k-calendar-table" [style.table-layout]="'auto'" *ngIf="isMonthView()">
<thead class="k-calendar-thead">
<tr class="k-calendar-tr">
<th class="k-calendar-th" *ngFor="let name of weekNames; let i = index;" scope="col" [attr.aria-label]="wideWeekNames[i]" role="columnheader">{{name}}</th>
</tr>
</thead>
</table>
<kendo-virtualization
[tabindex]="-1"
[skip]="skip"
[take]="take"
[total]="total"
[itemHeight]="viewHeight"
[topOffset]="viewOffset"
[bottomOffset]="bottomOffset"
[scrollOffsetSize]="viewOffset"
[maxScrollDifference]="viewHeight"
(pageChange)="onPageChange($event)"
(scrollChange)="scrollChange($event)"
(activeIndexChange)="setActiveDate($event)"
>
<table
#list
role="grid"
class="k-calendar-table"
[attr.tabindex]="getTabIndex()"
[attr.aria-labelledby]="id"
(focus)="focusCalendar.emit()"
(blur)="blurCalendar.emit($event)"
>
<colgroup><col *ngFor="let _ of cols" /></colgroup>
<tbody class="k-calendar-tbody"
*kFor="let date of dates"
kendoCalendarView
[allowReverse]="allowReverse"
[activeRangeEnd]="activeRangeEnd"
[selectionRange]="selectionRange"
[showOtherMonthDays]="showOtherMonthDays"
[headerTitle]="headerTitle"
role="rowgroup"
[activeView]="activeView"
[isActive]="isActive"
[min]="min" [max]="max"
[cellUID]="cellUID"
[focusedDate]="focusedDate"
[selectedDates]="selectedDates"
[weekNumber]="weekNumber"
[templateRef]="cellTemplateRef"
[weekNumberTemplateRef]="weekNumberTemplateRef"
[viewDate]="date"
(cellClick)="cellClick.emit($event)"
(weekNumberCellClick)="weekNumberCellClick.emit($event)"
(focusedCellId)="focusedCellChange.emit($event)"
(cellEnter)="cellEnter.emit($event)"
></tbody>
</table>
</kendo-virtualization>
<kendo-calendar-footer
*ngIf="showFooter"
[footerTemplateRef]="footerTemplateRef"
[activeViewValue]="activeViewValue"
[currentDate]="activeDate">
</kendo-calendar-footer>
`,
standalone: true,
imports: [HeaderComponent, NgIf, NgFor, VirtualizationComponent, KForOf, ViewComponent, FooterComponent]
}]
}], ctorParameters: function () { return [{ type: i1.BusViewService }, { type: i0.ChangeDetectorRef }, { type: i2.IntlService }, { type: i3.CalendarDOMService }, { type: i0.Renderer2 }]; }, propDecorators: { allowReverse: [{
type: Input
}], cellTemplateRef: [{
type: Input
}], weekNumberTemplateRef: [{
type: Input
}], headerTitleTemplateRef: [{
type: Input
}], headerTemplateRef: [{
type: Input
}], footerTemplateRef: [{
type: Input
}], showOtherMonthDays: [{
type: Input
}], activeView: [{
type: Input
}], cellUID: [{
type: Input
}], focusedDate: [{
type: Input
}], isActive: [{
type: Input
}], min: [{
type: Input
}], max: [{
type: Input
}], selectedDates: [{
type: Input
}], tabIndex: [{
type: Input
}], disabled: [{
type: Input
}], id: [{
type: Input
}], showFooter: [{
type: Input
}], weekDaysFormat: [{
type: Input
}], activeRangeEnd: [{
type: Input
}], selectionRange: [{
type: Input
}], size: [{
type: Input
}], weekNumber: [{
type: Input
}], cellEnter: [{
type: Output
}], cellClick: [{
type: Output
}], weekNumberCellClick: [{
type: Output
}], activeDateChange: [{
type: Output
}], todayButtonClick: [{
type: Output
}], pageChange: [{
type: Output
}], focusCalendar: [{
type: Output
}], blurCalendar: [{
type: Output
}], focusedCellChange: [{
type: Output
}], virtualization: [{
type: ViewChild,
args: [VirtualizationComponent, { static: false }]
}], headerComponent: [{
type: ViewChild,
args: [HeaderComponent, { static: false }]
}], list: [{
type: ViewChild,
args: ['list', { static: true }]
}], getComponentClass: [{
type: HostBinding,
args: ["class.k-vstack"]
}, {
type: HostBinding,
args: ["class.k-calendar-view"]
}], getComponentMonthClass: [{
type: HostBinding,
args: ["class.k-calendar-monthview"]
}], getComponentYearClass: [{
type: HostBinding,
args: ["class.k-calendar-yearview"]
}], getComponentDecadeClass: [{
type: HostBinding,
args: ["class.k-calendar-decadeview"]
}], getComponentCenturyClass: [{
type: HostBinding,
args: ["class.k-calendar-centuryview"]
}] } });