UNPKG

angular-calendar

Version:

A calendar component for angular 15.0+ that can display events on a month, week or day view

1,211 lines (1,191 loc) 203 kB
import * as i0 from '@angular/core'; import { EventEmitter, Directive, Inject, Input, Output, Injectable, LOCALE_ID, Pipe, Component, HostListener, InjectionToken, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { DOCUMENT, formatDate, CommonModule, I18nPluralPipe } from '@angular/common'; import { Subject, Observable, of, timer, BehaviorSubject, interval } from 'rxjs'; import { takeUntil, switchMap, startWith, switchMapTo, map } from 'rxjs/operators'; import { positionElements } from 'positioning'; import { validateEvents as validateEvents$1, getMonthView, getWeekViewHeader, getWeekView } from 'calendar-utils'; export { DAYS_OF_WEEK } from 'calendar-utils'; import * as i2 from 'angular-draggable-droppable'; import { DragAndDropModule } from 'angular-draggable-droppable'; import { trigger, state, style, transition, animate } from '@angular/animations'; import * as i4 from 'angular-resizable-element'; import { ResizableModule } from 'angular-resizable-element'; class ClickDirective { constructor(renderer, elm, document) { this.renderer = renderer; this.elm = elm; this.document = document; this.clickListenerDisabled = false; this.click = new EventEmitter(); // eslint-disable-line this.destroy$ = new Subject(); } ngOnInit() { if (!this.clickListenerDisabled) { this.listen() .pipe(takeUntil(this.destroy$)) .subscribe((event) => { event.stopPropagation(); this.click.emit(event); }); } } ngOnDestroy() { this.destroy$.next(); } listen() { return new Observable((observer) => { return this.renderer.listen(this.elm.nativeElement, 'click', (event) => { observer.next(event); }); }); } } ClickDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: ClickDirective, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive }); ClickDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: ClickDirective, selector: "[mwlClick]", inputs: { clickListenerDisabled: "clickListenerDisabled" }, outputs: { click: "mwlClick" }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: ClickDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlClick]', }] }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }]; }, propDecorators: { clickListenerDisabled: [{ type: Input }], click: [{ type: Output, args: ['mwlClick'] }] } }); class KeydownEnterDirective { constructor(host, ngZone, renderer) { this.host = host; this.ngZone = ngZone; this.renderer = renderer; this.keydown = new EventEmitter(); // eslint-disable-line this.keydownListener = null; } ngOnInit() { this.ngZone.runOutsideAngular(() => { this.keydownListener = this.renderer.listen(this.host.nativeElement, 'keydown', (event) => { if (event.keyCode === 13 || event.which === 13 || event.key === 'Enter') { event.preventDefault(); event.stopPropagation(); this.ngZone.run(() => { this.keydown.emit(event); }); } }); }); } ngOnDestroy() { if (this.keydownListener !== null) { this.keydownListener(); this.keydownListener = null; } } } KeydownEnterDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: KeydownEnterDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); KeydownEnterDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: KeydownEnterDirective, selector: "[mwlKeydownEnter]", outputs: { keydown: "mwlKeydownEnter" }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: KeydownEnterDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlKeydownEnter]', }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }]; }, propDecorators: { keydown: [{ type: Output, args: ['mwlKeydownEnter'] }] } }); /** * This class is responsible for adding accessibility to the calendar. * You may override any of its methods via angulars DI to suit your requirements. * For example: * * ```typescript * import { A11yParams, CalendarA11y } from 'angular-calendar'; * import { formatDate, I18nPluralPipe } from '@angular/common'; * import { Injectable } from '@angular/core'; * * // adding your own a11y params * export interface CustomA11yParams extends A11yParams { * isDrSuess?: boolean; * } * * @Injectable() * export class CustomCalendarA11y extends CalendarA11y { * constructor(protected i18nPlural: I18nPluralPipe) { * super(i18nPlural); * } * * // overriding a function * public openDayEventsLandmark({ date, locale, isDrSuess }: CustomA11yParams): string { * if (isDrSuess) { * return ` * ${formatDate(date, 'EEEE MMMM d', locale)} * Today you are you! That is truer than true! There is no one alive * who is you-er than you! * `; * } * } * } * * // in your component that uses the calendar * providers: [{ * provide: CalendarA11y, * useClass: CustomCalendarA11y * }] * ``` */ class CalendarA11y { constructor(i18nPlural) { this.i18nPlural = i18nPlural; } /** * Aria label for the badges/date of a cell * @example: `Saturday October 19 1 event click to expand` */ monthCell({ day, locale }) { if (day.badgeTotal > 0) { return ` ${formatDate(day.date, 'EEEE MMMM d', locale)}, ${this.i18nPlural.transform(day.badgeTotal, { '=0': 'No events', '=1': 'One event', other: '# events', })}, click to expand `; } else { return `${formatDate(day.date, 'EEEE MMMM d', locale)}`; } } /** * Aria label for the open day events start landmark * @example: `Saturday October 19 expanded view` */ openDayEventsLandmark({ date, locale }) { return ` Beginning of expanded view for ${formatDate(date, 'EEEE MMMM dd', locale)} `; } /** * Aria label for alert that a day in the month view was expanded * @example: `Saturday October 19 expanded` */ openDayEventsAlert({ date, locale }) { return `${formatDate(date, 'EEEE MMMM dd', locale)} expanded`; } /** * Descriptive aria label for an event * @example: `Saturday October 19th, Scott's Pizza Party, from 11:00am to 5:00pm` */ eventDescription({ event, locale }) { if (event.allDay === true) { return this.allDayEventDescription({ event, locale }); } const aria = ` ${formatDate(event.start, 'EEEE MMMM dd', locale)}, ${event.title}, from ${formatDate(event.start, 'hh:mm a', locale)} `; if (event.end) { return aria + ` to ${formatDate(event.end, 'hh:mm a', locale)}`; } return aria; } /** * Descriptive aria label for an all day event * @example: * `Scott's Party, event spans multiple days: start time October 19 5:00pm, no stop time` */ allDayEventDescription({ event, locale }) { const aria = ` ${event.title}, event spans multiple days: start time ${formatDate(event.start, 'MMMM dd hh:mm a', locale)} `; if (event.end) { return (aria + `, stop time ${formatDate(event.end, 'MMMM d hh:mm a', locale)}`); } return aria + `, no stop time`; } /** * Aria label for the calendar event actions icons * @returns 'Edit' for fa-pencil icons, and 'Delete' for fa-times icons */ actionButtonLabel({ action }) { return action.a11yLabel; } /** * @returns {number} Tab index to be given to month cells */ monthCellTabIndex() { return 0; } /** * @returns true if the events inside the month cell should be aria-hidden */ hideMonthCellEvents() { return true; } /** * @returns true if event titles should be aria-hidden (global) */ hideEventTitle() { return true; } /** * @returns true if hour segments in the week view should be aria-hidden */ hideWeekHourSegment() { return true; } /** * @returns true if hour segments in the day view should be aria-hidden */ hideDayHourSegment() { return true; } } CalendarA11y.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11y, deps: [{ token: i1.I18nPluralPipe }], target: i0.ɵɵFactoryTarget.Injectable }); CalendarA11y.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11y }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11y, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.I18nPluralPipe }]; } }); /** * This pipe is primarily for rendering aria labels. Example usage: * ```typescript * // where `myEvent` is a `CalendarEvent` and myLocale is a locale identifier * {{ { event: myEvent, locale: myLocale } | calendarA11y: 'eventDescription' }} * ``` */ class CalendarA11yPipe { constructor(calendarA11y, locale) { this.calendarA11y = calendarA11y; this.locale = locale; } transform(a11yParams, method) { a11yParams.locale = a11yParams.locale || this.locale; if (typeof this.calendarA11y[method] === 'undefined') { const allowedMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(CalendarA11y.prototype)).filter((iMethod) => iMethod !== 'constructor'); throw new Error(`${method} is not a valid a11y method. Can only be one of ${allowedMethods.join(', ')}`); } return this.calendarA11y[method](a11yParams); } } CalendarA11yPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11yPipe, deps: [{ token: CalendarA11y }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); CalendarA11yPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11yPipe, name: "calendarA11y" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarA11yPipe, decorators: [{ type: Pipe, args: [{ name: 'calendarA11y', }] }], ctorParameters: function () { return [{ type: CalendarA11y }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }]; } }); class CalendarEventActionsComponent { constructor() { this.trackByActionId = (index, action) => action.id ? action.id : action; } } CalendarEventActionsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); CalendarEventActionsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.3", type: CalendarEventActionsComponent, selector: "mwl-calendar-event-actions", inputs: { event: "event", customTemplate: "customTemplate" }, ngImport: i0, template: ` <ng-template #defaultTemplate let-event="event" let-trackByActionId="trackByActionId" > <span *ngIf="event.actions" class="cal-event-actions"> <a class="cal-event-action" href="javascript:;" *ngFor="let action of event.actions; trackBy: trackByActionId" (mwlClick)="action.onClick({ event: event, sourceEvent: $event })" (mwlKeydownEnter)=" action.onClick({ event: event, sourceEvent: $event }) " [ngClass]="action.cssClass" [innerHtml]="action.label" tabindex="0" role="button" [attr.aria-label]=" { action: action } | calendarA11y : 'actionButtonLabel' " > </a> </span> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ event: event, trackByActionId: trackByActionId }" > </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ClickDirective, selector: "[mwlClick]", inputs: ["clickListenerDisabled"], outputs: ["mwlClick"] }, { kind: "directive", type: KeydownEnterDirective, selector: "[mwlKeydownEnter]", outputs: ["mwlKeydownEnter"] }, { kind: "pipe", type: CalendarA11yPipe, name: "calendarA11y" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventActionsComponent, decorators: [{ type: Component, args: [{ selector: 'mwl-calendar-event-actions', template: ` <ng-template #defaultTemplate let-event="event" let-trackByActionId="trackByActionId" > <span *ngIf="event.actions" class="cal-event-actions"> <a class="cal-event-action" href="javascript:;" *ngFor="let action of event.actions; trackBy: trackByActionId" (mwlClick)="action.onClick({ event: event, sourceEvent: $event })" (mwlKeydownEnter)=" action.onClick({ event: event, sourceEvent: $event }) " [ngClass]="action.cssClass" [innerHtml]="action.label" tabindex="0" role="button" [attr.aria-label]=" { action: action } | calendarA11y : 'actionButtonLabel' " > </a> </span> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ event: event, trackByActionId: trackByActionId }" > </ng-template> `, }] }], propDecorators: { event: [{ type: Input }], customTemplate: [{ type: Input }] } }); /** * This class is responsible for displaying all event titles within the calendar. You may override any of its methods via angulars DI to suit your requirements. For example: * * ```typescript * import { Injectable } from '@angular/core'; * import { CalendarEventTitleFormatter, CalendarEvent } from 'angular-calendar'; * * @Injectable() * class CustomEventTitleFormatter extends CalendarEventTitleFormatter { * * month(event: CalendarEvent): string { * return `Custom prefix: ${event.title}`; * } * * } * * // in your component * providers: [{ * provide: CalendarEventTitleFormatter, * useClass: CustomEventTitleFormatter * }] * ``` */ class CalendarEventTitleFormatter { /** * The month view event title. */ month(event, title) { return event.title; } /** * The month view event tooltip. Return a falsey value from this to disable the tooltip. */ monthTooltip(event, title) { return event.title; } /** * The week view event title. */ week(event, title) { return event.title; } /** * The week view event tooltip. Return a falsey value from this to disable the tooltip. */ weekTooltip(event, title) { return event.title; } /** * The day view event title. */ day(event, title) { return event.title; } /** * The day view event tooltip. Return a falsey value from this to disable the tooltip. */ dayTooltip(event, title) { return event.title; } } class CalendarEventTitlePipe { constructor(calendarEventTitle) { this.calendarEventTitle = calendarEventTitle; } transform(title, titleType, event) { return this.calendarEventTitle[titleType](event, title); } } CalendarEventTitlePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventTitlePipe, deps: [{ token: CalendarEventTitleFormatter }], target: i0.ɵɵFactoryTarget.Pipe }); CalendarEventTitlePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventTitlePipe, name: "calendarEventTitle" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventTitlePipe, decorators: [{ type: Pipe, args: [{ name: 'calendarEventTitle', }] }], ctorParameters: function () { return [{ type: CalendarEventTitleFormatter }]; } }); class CalendarEventTitleComponent { } CalendarEventTitleComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); CalendarEventTitleComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.3", type: CalendarEventTitleComponent, selector: "mwl-calendar-event-title", inputs: { event: "event", customTemplate: "customTemplate", view: "view" }, ngImport: i0, template: ` <ng-template #defaultTemplate let-event="event" let-view="view"> <span class="cal-event-title" [innerHTML]="event.title | calendarEventTitle : view : event" [attr.aria-hidden]="{} | calendarA11y : 'hideEventTitle'" > </span> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ event: event, view: view }" > </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: CalendarEventTitlePipe, name: "calendarEventTitle" }, { kind: "pipe", type: CalendarA11yPipe, name: "calendarA11y" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarEventTitleComponent, decorators: [{ type: Component, args: [{ selector: 'mwl-calendar-event-title', template: ` <ng-template #defaultTemplate let-event="event" let-view="view"> <span class="cal-event-title" [innerHTML]="event.title | calendarEventTitle : view : event" [attr.aria-hidden]="{} | calendarA11y : 'hideEventTitle'" > </span> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ event: event, view: view }" > </ng-template> `, }] }], propDecorators: { event: [{ type: Input }], customTemplate: [{ type: Input }], view: [{ type: Input }] } }); class CalendarTooltipWindowComponent { } CalendarTooltipWindowComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTooltipWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); CalendarTooltipWindowComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.3", type: CalendarTooltipWindowComponent, selector: "mwl-calendar-tooltip-window", inputs: { contents: "contents", placement: "placement", event: "event", customTemplate: "customTemplate" }, ngImport: i0, template: ` <ng-template #defaultTemplate let-contents="contents" let-placement="placement" let-event="event" > <div class="cal-tooltip" [ngClass]="'cal-tooltip-' + placement"> <div class="cal-tooltip-arrow"></div> <div class="cal-tooltip-inner" [innerHtml]="contents"></div> </div> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ contents: contents, placement: placement, event: event }" > </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTooltipWindowComponent, decorators: [{ type: Component, args: [{ selector: 'mwl-calendar-tooltip-window', template: ` <ng-template #defaultTemplate let-contents="contents" let-placement="placement" let-event="event" > <div class="cal-tooltip" [ngClass]="'cal-tooltip-' + placement"> <div class="cal-tooltip-arrow"></div> <div class="cal-tooltip-inner" [innerHtml]="contents"></div> </div> </ng-template> <ng-template [ngTemplateOutlet]="customTemplate || defaultTemplate" [ngTemplateOutletContext]="{ contents: contents, placement: placement, event: event }" > </ng-template> `, }] }], propDecorators: { contents: [{ type: Input }], placement: [{ type: Input }], event: [{ type: Input }], customTemplate: [{ type: Input }] } }); class CalendarTooltipDirective { constructor(elementRef, injector, renderer, componentFactoryResolver, viewContainerRef, document // eslint-disable-line ) { this.elementRef = elementRef; this.injector = injector; this.renderer = renderer; this.viewContainerRef = viewContainerRef; this.document = document; this.placement = 'auto'; // eslint-disable-line @angular-eslint/no-input-rename this.delay = null; // eslint-disable-line @angular-eslint/no-input-rename this.cancelTooltipDelay$ = new Subject(); this.tooltipFactory = componentFactoryResolver.resolveComponentFactory(CalendarTooltipWindowComponent); } ngOnChanges(changes) { if (this.tooltipRef && (changes.contents || changes.customTemplate || changes.event)) { this.tooltipRef.instance.contents = this.contents; this.tooltipRef.instance.customTemplate = this.customTemplate; this.tooltipRef.instance.event = this.event; this.tooltipRef.changeDetectorRef.markForCheck(); if (!this.contents) { this.hide(); } } } ngOnDestroy() { this.hide(); } onMouseOver() { const delay$ = this.delay === null ? of('now') : timer(this.delay); delay$.pipe(takeUntil(this.cancelTooltipDelay$)).subscribe(() => { this.show(); }); } onMouseOut() { this.hide(); } show() { if (!this.tooltipRef && this.contents) { this.tooltipRef = this.viewContainerRef.createComponent(this.tooltipFactory, 0, this.injector, []); this.tooltipRef.instance.contents = this.contents; this.tooltipRef.instance.customTemplate = this.customTemplate; this.tooltipRef.instance.event = this.event; if (this.appendToBody) { this.document.body.appendChild(this.tooltipRef.location.nativeElement); } requestAnimationFrame(() => { this.positionTooltip(); }); } } hide() { if (this.tooltipRef) { this.viewContainerRef.remove(this.viewContainerRef.indexOf(this.tooltipRef.hostView)); this.tooltipRef = null; } this.cancelTooltipDelay$.next(); } positionTooltip(previousPositions = []) { if (this.tooltipRef) { this.tooltipRef.changeDetectorRef.detectChanges(); this.tooltipRef.instance.placement = positionElements(this.elementRef.nativeElement, this.tooltipRef.location.nativeElement.children[0], this.placement, this.appendToBody); // keep re-positioning the tooltip until the arrow position doesn't make a difference if (previousPositions.indexOf(this.tooltipRef.instance.placement) === -1) { this.positionTooltip([ ...previousPositions, this.tooltipRef.instance.placement, ]); } } } } CalendarTooltipDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTooltipDirective, deps: [{ token: i0.ElementRef }, { token: i0.Injector }, { token: i0.Renderer2 }, { token: i0.ComponentFactoryResolver }, { token: i0.ViewContainerRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive }); CalendarTooltipDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: CalendarTooltipDirective, selector: "[mwlCalendarTooltip]", inputs: { contents: ["mwlCalendarTooltip", "contents"], placement: ["tooltipPlacement", "placement"], customTemplate: ["tooltipTemplate", "customTemplate"], event: ["tooltipEvent", "event"], appendToBody: ["tooltipAppendToBody", "appendToBody"], delay: ["tooltipDelay", "delay"] }, host: { listeners: { "mouseenter": "onMouseOver()", "mouseleave": "onMouseOut()" } }, usesOnChanges: true, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTooltipDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlCalendarTooltip]', }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Injector }, { type: i0.Renderer2 }, { type: i0.ComponentFactoryResolver }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }]; }, propDecorators: { contents: [{ type: Input, args: ['mwlCalendarTooltip'] }], placement: [{ type: Input, args: ['tooltipPlacement'] }], customTemplate: [{ type: Input, args: ['tooltipTemplate'] }], event: [{ type: Input, args: ['tooltipEvent'] }], appendToBody: [{ type: Input, args: ['tooltipAppendToBody'] }], delay: [{ type: Input, args: ['tooltipDelay'] }], onMouseOver: [{ type: HostListener, args: ['mouseenter'] }], onMouseOut: [{ type: HostListener, args: ['mouseleave'] }] } }); var CalendarView; (function (CalendarView) { CalendarView["Month"] = "month"; CalendarView["Week"] = "week"; CalendarView["Day"] = "day"; })(CalendarView || (CalendarView = {})); const validateEvents = (events) => { const warn = (...args) => console.warn('angular-calendar', ...args); return validateEvents$1(events, warn); }; function isInsideLeftAndRight(outer, inner) { return (Math.floor(outer.left) <= Math.ceil(inner.left) && Math.floor(inner.left) <= Math.ceil(outer.right) && Math.floor(outer.left) <= Math.ceil(inner.right) && Math.floor(inner.right) <= Math.ceil(outer.right)); } function isInsideTopAndBottom(outer, inner) { return (Math.floor(outer.top) <= Math.ceil(inner.top) && Math.floor(inner.top) <= Math.ceil(outer.bottom) && Math.floor(outer.top) <= Math.ceil(inner.bottom) && Math.floor(inner.bottom) <= Math.ceil(outer.bottom)); } function isInside(outer, inner) { return (isInsideLeftAndRight(outer, inner) && isInsideTopAndBottom(outer, inner)); } function roundToNearest(amount, precision) { return Math.round(amount / precision) * precision; } const trackByEventId = (index, event) => event.id ? event.id : event; const trackByWeekDayHeaderDate = (index, day) => day.date.toISOString(); const trackByHourSegment = (index, segment) => segment.date.toISOString(); const trackByHour = (index, hour) => hour.segments[0].date.toISOString(); const trackByWeekAllDayEvent = (index, weekEvent) => (weekEvent.event.id ? weekEvent.event.id : weekEvent.event); const trackByWeekTimeEvent = (index, weekEvent) => (weekEvent.event.id ? weekEvent.event.id : weekEvent.event); const MINUTES_IN_HOUR = 60; function getPixelAmountInMinutes(hourSegments, hourSegmentHeight, hourDuration) { return (hourDuration || MINUTES_IN_HOUR) / (hourSegments * hourSegmentHeight); } function getMinutesMoved(movedY, hourSegments, hourSegmentHeight, eventSnapSize, hourDuration) { const draggedInPixelsSnapSize = roundToNearest(movedY, eventSnapSize || hourSegmentHeight); const pixelAmountInMinutes = getPixelAmountInMinutes(hourSegments, hourSegmentHeight, hourDuration); return draggedInPixelsSnapSize * pixelAmountInMinutes; } function getDefaultEventEnd(dateAdapter, event, minimumMinutes) { if (event.end) { return event.end; } else { return dateAdapter.addMinutes(event.start, minimumMinutes); } } function addDaysWithExclusions(dateAdapter, date, days, excluded) { let daysCounter = 0; let daysToAdd = 0; const changeDays = days < 0 ? dateAdapter.subDays : dateAdapter.addDays; let result = date; while (daysToAdd <= Math.abs(days)) { result = changeDays(date, daysCounter); const day = dateAdapter.getDay(result); if (excluded.indexOf(day) === -1) { daysToAdd++; } daysCounter++; } return result; } function isDraggedWithinPeriod(newStart, newEnd, period) { const end = newEnd || newStart; return ((period.start <= newStart && newStart <= period.end) || (period.start <= end && end <= period.end)); } function shouldFireDroppedEvent(dropEvent, date, allDay, calendarId) { return (dropEvent.dropData && dropEvent.dropData.event && (dropEvent.dropData.calendarId !== calendarId || (dropEvent.dropData.event.allDay && !allDay) || (!dropEvent.dropData.event.allDay && allDay))); } function getWeekViewPeriod(dateAdapter, viewDate, weekStartsOn, excluded = [], daysInWeek) { let viewStart = daysInWeek ? dateAdapter.startOfDay(viewDate) : dateAdapter.startOfWeek(viewDate, { weekStartsOn }); const endOfWeek = dateAdapter.endOfWeek(viewDate, { weekStartsOn }); while (excluded.indexOf(dateAdapter.getDay(viewStart)) > -1 && viewStart < endOfWeek) { viewStart = dateAdapter.addDays(viewStart, 1); } if (daysInWeek) { const viewEnd = dateAdapter.endOfDay(addDaysWithExclusions(dateAdapter, viewStart, daysInWeek - 1, excluded)); return { viewStart, viewEnd }; } else { let viewEnd = endOfWeek; while (excluded.indexOf(dateAdapter.getDay(viewEnd)) > -1 && viewEnd > viewStart) { viewEnd = dateAdapter.subDays(viewEnd, 1); } return { viewStart, viewEnd }; } } function isWithinThreshold({ x, y }) { const DRAG_THRESHOLD = 1; return Math.abs(x) > DRAG_THRESHOLD || Math.abs(y) > DRAG_THRESHOLD; } class DateAdapter { } /** * Change the view date to the previous view. For example: * * ```typescript * <button * mwlCalendarPreviousView * [(viewDate)]="viewDate" * [view]="view"> * Previous * </button> * ``` */ class CalendarPreviousViewDirective { constructor(dateAdapter) { this.dateAdapter = dateAdapter; /** * Days to skip when going back by 1 day */ this.excludeDays = []; /** * Called when the view date is changed */ this.viewDateChange = new EventEmitter(); } /** * @hidden */ onClick() { const subFn = { day: this.dateAdapter.subDays, week: this.dateAdapter.subWeeks, month: this.dateAdapter.subMonths, }[this.view]; if (this.view === CalendarView.Day) { this.viewDateChange.emit(addDaysWithExclusions(this.dateAdapter, this.viewDate, -1, this.excludeDays)); } else if (this.view === CalendarView.Week && this.daysInWeek) { this.viewDateChange.emit(addDaysWithExclusions(this.dateAdapter, this.viewDate, -this.daysInWeek, this.excludeDays)); } else { this.viewDateChange.emit(subFn(this.viewDate, 1)); } } } CalendarPreviousViewDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarPreviousViewDirective, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Directive }); CalendarPreviousViewDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: CalendarPreviousViewDirective, selector: "[mwlCalendarPreviousView]", inputs: { view: "view", viewDate: "viewDate", excludeDays: "excludeDays", daysInWeek: "daysInWeek" }, outputs: { viewDateChange: "viewDateChange" }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarPreviousViewDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlCalendarPreviousView]', }] }], ctorParameters: function () { return [{ type: DateAdapter }]; }, propDecorators: { view: [{ type: Input }], viewDate: [{ type: Input }], excludeDays: [{ type: Input }], daysInWeek: [{ type: Input }], viewDateChange: [{ type: Output }], onClick: [{ type: HostListener, args: ['click'] }] } }); /** * Change the view date to the next view. For example: * * ```typescript * <button * mwlCalendarNextView * [(viewDate)]="viewDate" * [view]="view"> * Next * </button> * ``` */ class CalendarNextViewDirective { constructor(dateAdapter) { this.dateAdapter = dateAdapter; /** * Days to skip when going forward by 1 day */ this.excludeDays = []; /** * Called when the view date is changed */ this.viewDateChange = new EventEmitter(); } /** * @hidden */ onClick() { const addFn = { day: this.dateAdapter.addDays, week: this.dateAdapter.addWeeks, month: this.dateAdapter.addMonths, }[this.view]; if (this.view === CalendarView.Day) { this.viewDateChange.emit(addDaysWithExclusions(this.dateAdapter, this.viewDate, 1, this.excludeDays)); } else if (this.view === CalendarView.Week && this.daysInWeek) { this.viewDateChange.emit(addDaysWithExclusions(this.dateAdapter, this.viewDate, this.daysInWeek, this.excludeDays)); } else { this.viewDateChange.emit(addFn(this.viewDate, 1)); } } } CalendarNextViewDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarNextViewDirective, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Directive }); CalendarNextViewDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: CalendarNextViewDirective, selector: "[mwlCalendarNextView]", inputs: { view: "view", viewDate: "viewDate", excludeDays: "excludeDays", daysInWeek: "daysInWeek" }, outputs: { viewDateChange: "viewDateChange" }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarNextViewDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlCalendarNextView]', }] }], ctorParameters: function () { return [{ type: DateAdapter }]; }, propDecorators: { view: [{ type: Input }], viewDate: [{ type: Input }], excludeDays: [{ type: Input }], daysInWeek: [{ type: Input }], viewDateChange: [{ type: Output }], onClick: [{ type: HostListener, args: ['click'] }] } }); /** * Change the view date to the current day. For example: * * ```typescript * <button * mwlCalendarToday * [(viewDate)]="viewDate"> * Today * </button> * ``` */ class CalendarTodayDirective { constructor(dateAdapter) { this.dateAdapter = dateAdapter; /** * Called when the view date is changed */ this.viewDateChange = new EventEmitter(); } /** * @hidden */ onClick() { this.viewDateChange.emit(this.dateAdapter.startOfDay(new Date())); } } CalendarTodayDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTodayDirective, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Directive }); CalendarTodayDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.3", type: CalendarTodayDirective, selector: "[mwlCalendarToday]", inputs: { viewDate: "viewDate" }, outputs: { viewDateChange: "viewDateChange" }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarTodayDirective, decorators: [{ type: Directive, args: [{ selector: '[mwlCalendarToday]', }] }], ctorParameters: function () { return [{ type: DateAdapter }]; }, propDecorators: { viewDate: [{ type: Input }], viewDateChange: [{ type: Output }], onClick: [{ type: HostListener, args: ['click'] }] } }); /** * This will use the angular date pipe to do all date formatting. It is the default date formatter used by the calendar. */ class CalendarAngularDateFormatter { constructor(dateAdapter) { this.dateAdapter = dateAdapter; } /** * The month view header week day labels */ monthViewColumnHeader({ date, locale }) { return formatDate(date, 'EEEE', locale); } /** * The month view cell day number */ monthViewDayNumber({ date, locale }) { return formatDate(date, 'd', locale); } /** * The month view title */ monthViewTitle({ date, locale }) { return formatDate(date, 'LLLL y', locale); } /** * The week view header week day labels */ weekViewColumnHeader({ date, locale }) { return formatDate(date, 'EEEE', locale); } /** * The week view sub header day and month labels */ weekViewColumnSubHeader({ date, locale, }) { return formatDate(date, 'MMM d', locale); } /** * The week view title */ weekViewTitle({ date, locale, weekStartsOn, excludeDays, daysInWeek, }) { const { viewStart, viewEnd } = getWeekViewPeriod(this.dateAdapter, date, weekStartsOn, excludeDays, daysInWeek); const format = (dateToFormat, showYear) => formatDate(dateToFormat, 'MMM d' + (showYear ? ', yyyy' : ''), locale); return `${format(viewStart, viewStart.getUTCFullYear() !== viewEnd.getUTCFullYear())} - ${format(viewEnd, true)}`; } /** * The time formatting down the left hand side of the week view */ weekViewHour({ date, locale }) { return formatDate(date, 'h a', locale); } /** * The time formatting down the left hand side of the day view */ dayViewHour({ date, locale }) { return formatDate(date, 'h a', locale); } /** * The day view title */ dayViewTitle({ date, locale }) { return formatDate(date, 'EEEE, MMMM d, y', locale); } } CalendarAngularDateFormatter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarAngularDateFormatter, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable }); CalendarAngularDateFormatter.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarAngularDateFormatter }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarAngularDateFormatter, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: DateAdapter }]; } }); /** * This class is responsible for all formatting of dates. There are 3 implementations available, the `CalendarAngularDateFormatter` (default) which uses the angular date pipe to format dates, the `CalendarNativeDateFormatter` which will use the <a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl" target="_blank">Intl</a> API to format dates, or there is the `CalendarMomentDateFormatter` which uses <a href="http://momentjs.com/" target="_blank">moment</a>. * * If you wish, you may override any of the defaults via angulars DI. For example: * * ```typescript * import { CalendarDateFormatter, DateFormatterParams } from 'angular-calendar'; * import { formatDate } from '@angular/common'; * import { Injectable } from '@angular/core'; * * @Injectable() * class CustomDateFormatter extends CalendarDateFormatter { * * public monthViewColumnHeader({date, locale}: DateFormatterParams): string { * return formatDate(date, 'EEE', locale); // use short week days * } * * } * * // in your component that uses the calendar * providers: [{ * provide: CalendarDateFormatter, * useClass: CustomDateFormatter * }] * ``` */ class CalendarDateFormatter extends CalendarAngularDateFormatter { } CalendarDateFormatter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarDateFormatter, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); CalendarDateFormatter.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarDateFormatter }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarDateFormatter, decorators: [{ type: Injectable }] }); /** * This pipe is primarily for rendering the current view title. Example usage: * ```typescript * // where `viewDate` is a `Date` and view is `'month' | 'week' | 'day'` * {{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }} * ``` */ class CalendarDatePipe { constructor(dateFormatter, locale) { this.dateFormatter = dateFormatter; this.locale = locale; } transform(date, method, locale = this.locale, weekStartsOn = 0, excludeDays = [], daysInWeek) { if (typeof this.dateFormatter[method] === 'undefined') { const allowedMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(CalendarDateFormatter.prototype)).filter((iMethod) => iMethod !== 'constructor'); throw new Error(`${method} is not a valid date formatter. Can only be one of ${allowedMethods.join(', ')}`); } return this.dateFormatter[method]({ date, locale, weekStartsOn, excludeDays, daysInWeek, }); } } CalendarDatePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarDatePipe, deps: [{ token: CalendarDateFormatter }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Pipe }); CalendarDatePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.0.3", ngImport: i0, type: CalendarDatePipe, name: "calendarDate" }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarDatePipe, decorators: [{ type: Pipe, args: [{ name: 'calendarDate', }] }], ctorParameters: function () { return [{ type: CalendarDateFormatter }, { type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID] }] }]; } }); class CalendarUtils { constructor(dateAdapter) { this.dateAdapter = dateAdapter; } getMonthView(args) { return getMonthView(this.dateAdapter, args); } getWeekViewHeader(args) { return getWeekViewHeader(this.dateAdapter, args); } getWeekView(args) { return getWeekView(this.dateAdapter, args); } } CalendarUtils.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarUtils, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable }); CalendarUtils.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarUtils }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarUtils, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: DateAdapter }]; } }); const MOMENT = new InjectionToken('Moment'); /** * This will use <a href="http://momentjs.com/" target="_blank">moment</a> to do all date formatting. To use this class: * * ```typescript * import { CalendarDateFormatter, CalendarMomentDateFormatter, MOMENT } from 'angular-calendar'; * import moment from 'moment'; * * // in your component * provide: [{ * provide: MOMENT, useValue: moment * }, { * provide: CalendarDateFormatter, useClass: CalendarMomentDateFormatter * }] * * ``` */ class CalendarMomentDateFormatter { /** * @hidden */ constructor(moment, dateAdapter) { this.moment = moment; this.dateAdapter = dateAdapter; } /** * The month view header week day labels */ monthViewColumnHeader({ date, locale }) { return this.moment(date).locale(locale).format('dddd'); } /** * The month view cell day number */ monthViewDayNumber({ date, locale }) { return this.moment(date).locale(locale).format('D'); } /** * The month view title */ monthViewTitle({ date, locale }) { return this.moment(date).locale(locale).format('MMMM YYYY'); } /** * The week view header week day labels */ weekViewColumnHeader({ date, locale }) { return this.moment(date).locale(locale).format('dddd'); } /** * The week view sub header day and month labels */ weekViewColumnSubHeader({ date, locale, }) { return this.moment(date).locale(locale).format('MMM D'); } /** * The week view title */ weekViewTitle({ date, locale, weekStartsOn, excludeDays, daysInWeek, }) { const { viewStart, viewEnd } = getWeekViewPeriod(this.dateAdapter, date, weekStartsOn, excludeDays, daysInWeek); const format = (dateToFormat, showYear) => this.moment(dateToFormat) .locale(locale) .format('MMM D' + (showYear ? ', YYYY' : '')); return `${format(viewStart, viewStart.getUTCFullYear() !== viewEnd.getUTCFullYear())} - ${format(viewEnd, true)}`; } /** * The time formatting down the left hand side of the week view */ weekViewHour({ date, locale }) { return this.moment(date).locale(locale).format('ha'); } /** * The time formatting down the left hand side of the day view */ dayViewHour({ date, locale }) { return this.moment(date).locale(locale).format('ha'); } /** * The day view title */ dayViewTitle({ date, locale }) { return this.moment(date).locale(locale).format('dddd, LL'); // dddd = Thursday } // LL = locale-dependent Month Day, Year } CalendarMomentDateFormatter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.3", ngImport: i0, type: CalendarMomentDateFormatter, deps: [{ token: MOMENT }, { token: DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable }); CalendarMomentDateFormatter.ɵp