novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
403 lines (351 loc) • 11.3 kB
text/typescript
import {
Component,
Input,
OnChanges,
Output,
EventEmitter,
ChangeDetectorRef,
LOCALE_ID,
Inject,
OnInit,
OnDestroy,
TemplateRef
} from '@angular/core';
import {
CalendarEvent,
DayView,
DayViewHour,
DayViewHourSegment,
DayViewEvent,
CalendarEventTimesChangedEvent,
getDayView,
getDayViewHourGrid
} from '../../../utils/calendar-utils/CalendarUtils';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
//import { ResizeEvent } from 'angular-resizable-element';
// import * as dateFns from 'date-fns';
// import { CalendarDragHelper } from '../../providers/calendarDragHelper.provider';
// import { CalendarResizeHelper } from '../../providers/calendarResizeHelper.provider';
// import { CalendarEventTimesChangedEvent } from '../../interfaces/calendarEventTimesChangedEvent.interface';
/**
* @hidden
*/
const SEGMENT_HEIGHT: number = 30;
/**
* @hidden
*/
const MINUTES_IN_HOUR: number = 60;
/**
* Shows all events on a given day. Example usage:
*
* ```typescript
* <novo-calendar-day
* [viewDate]="viewDate"
* [events]="events">
* </novo-calendar-day>
* ```
*/
export class NovoCalendarDayViewElement implements OnChanges, OnInit, OnDestroy {
/**
* The current view date
*/
viewDate: Date;
/**
* An array of events to display on view
*/
events: CalendarEvent[] = [];
/**
* The number of segments in an hour. Must be <= 6
*/
hourSegments: number = 2;
/**
* The day start hours in 24 hour time. Must be 0-23
*/
dayStartHour: number = 0;
/**
* The day start minutes. Must be 0-59
*/
dayStartMinute: number = 0;
/**
* The day end hours in 24 hour time. Must be 0-23
*/
dayEndHour: number = 23;
/**
* The day end minutes. Must be 0-59
*/
dayEndMinute: number = 59;
/**
* The width in pixels of each event on the view
*/
eventWidth: number = 150;
/**
* An observable that when emitted on will re-render the current view
*/
refresh: Subject<any>;
/**
* The locale used to format dates
*/
locale: string;
/**
* A function that will be called before each hour segment is called. The first argument will contain the hour segment.
* If you add the `cssClass` property to the segment it will add that class to the hour segment in the template
*/
hourSegmentModifier: Function;
/**
* The grid size to snap resizing and dragging of events to
*/
eventSnapSize: number = 30;
/**
* The placement of the event tooltip
*/
tooltipPosition: string = 'top';
/**
* A custom template to use to replace the hour segment
*/
hourSegmentTemplate: TemplateRef<any>;
/**
* A custom template to use for all day events
*/
allDayEventTemplate: TemplateRef<any>;
/**
* A custom template to use for day view events
*/
eventTemplate: TemplateRef<any>;
/**
* Called when an event title is clicked
*/
eventClicked: EventEmitter<{ event: CalendarEvent }> = new EventEmitter<{ event: CalendarEvent }>();
/**
* Called when an hour segment is clicked
*/
hourSegmentClicked: EventEmitter<{ date: Date }> = new EventEmitter<{ date: Date }>();
/**
* Called when an event is resized or dragged and dropped
*/
eventTimesChanged: EventEmitter<CalendarEventTimesChangedEvent> = new EventEmitter<CalendarEventTimesChangedEvent>();
/**
* @hidden
*/
hours: DayViewHour[] = [];
/**
* @hidden
*/
view: DayView;
/**
* @hidden
*/
width: number = 0;
/**
* @hidden
*/
refreshSubscription: Subscription;
/**
* @hidden
*/
currentResize: {
originalTop: number,
originalHeight: number,
edge: string
};
/**
* @hidden
*/
validateDrag: Function;
/**
* @hidden
*/
validateResize: Function;
/**
* @hidden
*/
constructor(private cdr: ChangeDetectorRef, locale: string) {
this.locale = locale;
}
/**
* @hidden
*/
ngOnInit(): void {
if (this.refresh) {
this.refreshSubscription = this.refresh.subscribe(() => {
this.refreshAll();
this.cdr.detectChanges();
});
}
}
/**
* @hidden
*/
ngOnDestroy(): void {
if (this.refreshSubscription) {
this.refreshSubscription.unsubscribe();
}
}
/**
* @hidden
*/
ngOnChanges(changes: any): void {
if (
changes.viewDate ||
changes.dayStartHour ||
changes.dayStartMinute ||
changes.dayEndHour ||
changes.dayEndMinute
) {
this.refreshHourGrid();
}
if (
changes.viewDate ||
changes.events ||
changes.dayStartHour ||
changes.dayStartMinute ||
changes.dayEndHour ||
changes.dayEndMinute ||
changes.eventWidth
) {
this.refreshView();
}
}
/*
eventDropped(dropEvent: {dropData?: {event?: CalendarEvent}}, segment: DayViewHourSegment): void {
if (dropEvent.dropData && dropEvent.dropData.event) {
this.eventTimesChanged.emit({event: dropEvent.dropData.event, newStart: segment.date});
}
}
resizeStarted(event: DayViewEvent, resizeEvent: ResizeEvent, dayViewContainer: HTMLElement): void {
this.currentResize = {
originalTop: event.top,
originalHeight: event.height,
edge: typeof resizeEvent.edges.top !== 'undefined' ? 'top' : 'bottom'
};
const resizeHelper: CalendarResizeHelper = new CalendarResizeHelper(dayViewContainer);
this.validateResize = ({rectangle}) => resizeHelper.validateResize({rectangle});
this.cdr.detectChanges();
}
resizing(event: DayViewEvent, resizeEvent: ResizeEvent): void {
if (resizeEvent.edges.top) {
event.top = this.currentResize.originalTop + +resizeEvent.edges.top;
event.height = this.currentResize.originalHeight - +resizeEvent.edges.top;
} else if (resizeEvent.edges.bottom) {
event.height = this.currentResize.originalHeight + +resizeEvent.edges.bottom;
}
}
resizeEnded(dayEvent: DayViewEvent): void {
let pixelsMoved: number;
if (this.currentResize.edge === 'top') {
pixelsMoved = (dayEvent.top - this.currentResize.originalTop);
} else {
pixelsMoved = (dayEvent.height - this.currentResize.originalHeight);
}
dayEvent.top = this.currentResize.originalTop;
dayEvent.height = this.currentResize.originalHeight;
const pixelAmountInMinutes: number = MINUTES_IN_HOUR / (this.hourSegments * SEGMENT_HEIGHT);
const minutesMoved: number = pixelsMoved * pixelAmountInMinutes;
let newStart: Date = dayEvent.event.start;
let newEnd: Date = dayEvent.event.end;
if (this.currentResize.edge === 'top') {
newStart = addMinutes(newStart, minutesMoved);
} else if (newEnd) {
newEnd = addMinutes(newEnd, minutesMoved);
}
this.eventTimesChanged.emit({newStart, newEnd, event: dayEvent.event});
this.currentResize = null;
}
dragStart(event: HTMLElement, dayViewContainer: HTMLElement): void {
const dragHelper: CalendarDragHelper = new CalendarDragHelper(dayViewContainer, event);
this.validateDrag = ({x, y}) => !this.currentResize && dragHelper.validateDrag({x, y});
this.cdr.detectChanges();
}
eventDragged(dayEvent: DayViewEvent, draggedInPixels: number): void {
const pixelAmountInMinutes: number = MINUTES_IN_HOUR / (this.hourSegments * SEGMENT_HEIGHT);
const minutesMoved: number = draggedInPixels * pixelAmountInMinutes;
const newStart: Date = addMinutes(dayEvent.event.start, minutesMoved);
let newEnd: Date;
if (dayEvent.event.end) {
newEnd = addMinutes(dayEvent.event.end, minutesMoved);
}
this.eventTimesChanged.emit({newStart, newEnd, event: dayEvent.event});
}
*/
private refreshHourGrid(): void {
this.hours = getDayViewHourGrid({
viewDate: this.viewDate,
hourSegments: this.hourSegments,
dayStart: {
hour: this.dayStartHour,
minute: this.dayStartMinute
},
dayEnd: {
hour: this.dayEndHour,
minute: this.dayEndMinute
}
});
if (this.hourSegmentModifier) {
this.hours.forEach(hour => {
hour.segments.forEach(segment => this.hourSegmentModifier(segment));
});
}
}
private refreshView(): void {
this.view = getDayView({
events: this.events,
viewDate: this.viewDate,
hourSegments: this.hourSegments,
dayStart: {
hour: this.dayStartHour,
minute: this.dayStartMinute
},
dayEnd: {
hour: this.dayEndHour,
minute: this.dayEndMinute
},
eventWidth: this.eventWidth,
segmentHeight: SEGMENT_HEIGHT
});
}
private refreshAll(): void {
this.refreshHourGrid();
this.refreshView();
}
}