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
JavaScript
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