UNPKG

@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
/**----------------------------------------------------------------------------------------- * 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"] }] } });