novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
401 lines (350 loc) • 11 kB
text/typescript
import {
ChangeDetectorRef,
Component,
EventEmitter,
Inject,
Input,
LOCALE_ID,
OnChanges,
OnDestroy,
OnInit,
Output,
TemplateRef
} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
import {
CalendarEvent,
CalendarEventTimesChangedEvent,
DayViewHour,
getDayViewHourGrid,
getWeekView,
getWeekViewHeader,
WeekDay,
WeekViewEventRow
} from '../../../utils/calendar-utils/CalendarUtils';
/**
* @hidden
*/
const SEGMENT_HEIGHT: number = 30;
/**
* @hidden
*/
const MINUTES_IN_HOUR: number = 60;
/**
* Shows all events on a given week. Example usage:
*
* ```typescript
* <novo-calendar-week
* [viewDate]="viewDate"
* [events]="events">
* </novo-calendar-week>
* ```
*/
export class NovoCalendarWeekViewElement implements OnChanges, OnInit, OnDestroy {
/**
* The current view date
*/
viewDate: Date;
/**
* An array of events to display on view
*/
events: CalendarEvent[] = [];
/**
* An array of day indexes (0 = sunday, 1 = monday etc) that will be hidden on the view
*/
excludeDays: number[] = [];
/**
* An observable that when emitted on will re-render the current view
*/
refresh: Subject<any>;
/**
* The locale used to format dates
*/
locale: string;
/**
* The placement of the event tooltip
*/
tooltipPosition: string = 'bottom';
/**
* The start number of the week
*/
weekStartsOn: number;
/**
* A custom template to use to replace the header
*/
headerTemplate: TemplateRef<any>;
/**
* A custom template to use for week view events
*/
eventTemplate: TemplateRef<any>;
/**
* The precision to display events.
* `days` will round event start and end dates to the nearest day and `minutes` will not do this rounding
*/
precision: 'days' | 'minutes' = 'days';
/**
* 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;
/**
* A custom template to use to replace the hour segment
*/
hourSegmentTemplate: TemplateRef<any>;
/**
* Called when an hour segment is clicked
*/
hourSegmentClicked: EventEmitter<{ date: Date }> = new EventEmitter<{ date: Date }>();
/**
* Called when a header week day is clicked
*/
dayClicked: EventEmitter<{ date: Date }> = new EventEmitter<{ date: Date }>();
/**
* Called when the event title is clicked
*/
eventClicked: EventEmitter<{ event: CalendarEvent }> = new EventEmitter<{ event: CalendarEvent }>();
/**
* Called when an event is resized or dragged and dropped
*/
eventTimesChanged: EventEmitter<CalendarEventTimesChangedEvent> = new EventEmitter<CalendarEventTimesChangedEvent>();
/**
* @hidden
*/
days: WeekDay[];
/**
* @hidden
*/
hours: DayViewHour[] = [];
/**
* @hidden
*/
eventRows: WeekViewEventRow[] = [];
/**
* @hidden
*/
refreshSubscription: Subscription;
/**
* @hidden
*/
currentResize: {
originalOffset: number,
originalSpan: 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
*/
ngOnChanges(changes: any): void {
if (changes.viewDate || changes.excludeDays) {
this.refreshHeader();
}
if (changes.events || changes.viewDate || changes.excludeDays) {
this.refreshBody();
}
if (
changes.viewDate ||
changes.dayStartHour ||
changes.dayStartMinute ||
changes.dayEndHour ||
changes.dayEndMinute
) {
this.refreshHourGrid();
}
}
/**
* @hidden
*/
ngOnDestroy(): void {
if (this.refreshSubscription) {
this.refreshSubscription.unsubscribe();
}
}
/*
resizeStarted(weekViewContainer: HTMLElement, weekEvent: WeekViewEvent, resizeEvent: ResizeEvent): void {
this.currentResize = {
originalOffset: weekEvent.offset,
originalSpan: weekEvent.span,
edge: typeof resizeEvent.edges.left !== 'undefined' ? 'left' : 'right'
};
const resizeHelper: CalendarResizeHelper = new CalendarResizeHelper(weekViewContainer, this.getDayColumnWidth(weekViewContainer));
this.validateResize = ({rectangle}) => resizeHelper.validateResize({rectangle});
this.cdr.detectChanges();
}
resizing(weekEvent: WeekViewEvent, resizeEvent: ResizeEvent, dayWidth: number): void {
if (resizeEvent.edges.left) {
const diff: number = Math.round(+resizeEvent.edges.left / dayWidth);
weekEvent.offset = this.currentResize.originalOffset + diff;
weekEvent.span = this.currentResize.originalSpan - diff;
} else if (resizeEvent.edges.right) {
const diff: number = Math.round(+resizeEvent.edges.right / dayWidth);
weekEvent.span = this.currentResize.originalSpan + diff;
}
}
resizeEnded(weekEvent: WeekViewEvent): void {
let daysDiff: number;
if (this.currentResize.edge === 'left') {
daysDiff = weekEvent.offset - this.currentResize.originalOffset;
} else {
daysDiff = weekEvent.span - this.currentResize.originalSpan;
}
weekEvent.offset = this.currentResize.originalOffset;
weekEvent.span = this.currentResize.originalSpan;
let newStart: Date = weekEvent.event.start;
let newEnd: Date = weekEvent.event.end;
if (this.currentResize.edge === 'left') {
newStart = addDays(newStart, daysDiff);
} else if (newEnd) {
newEnd = addDays(newEnd, daysDiff);
}
this.eventTimesChanged.emit({newStart, newEnd, event: weekEvent.event});
this.currentResize = null;
}
eventDragged(weekEvent: WeekViewEvent, draggedByPx: number, dayWidth: number): void {
const daysDragged: number = draggedByPx / dayWidth;
const newStart: Date = addDays(weekEvent.event.start, daysDragged);
let newEnd: Date;
if (weekEvent.event.end) {
newEnd = addDays(weekEvent.event.end, daysDragged);
}
this.eventTimesChanged.emit({newStart, newEnd, event: weekEvent.event});
}
dragStart(weekViewContainer: HTMLElement, event: HTMLElement): void {
const dragHelper: CalendarDragHelper = new CalendarDragHelper(weekViewContainer, event);
this.validateDrag = ({x, y}) => !this.currentResize && dragHelper.validateDrag({x, y});
this.cdr.detectChanges();
}
*/
getDayColumnWidth(eventRowContainer: HTMLElement): number {
return Math.floor(eventRowContainer.offsetWidth / this.days.length);
}
private refreshHeader(): void {
this.days = getWeekViewHeader({
viewDate: this.viewDate,
weekStartsOn: this.weekStartsOn,
excluded: this.excludeDays
});
}
private refreshBody(): void {
this.eventRows = getWeekView({
events: this.events,
viewDate: this.viewDate,
weekStartsOn: this.weekStartsOn,
excluded: this.excludeDays,
hourSegments: this.hourSegments,
segmentHeight: SEGMENT_HEIGHT,
dayStart: {
hour: this.dayStartHour,
minute: this.dayStartMinute
},
dayEnd: {
hour: this.dayEndHour,
minute: this.dayEndMinute
},
//precision: this.precision
});
}
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 refreshAll(): void {
this.refreshHeader();
this.refreshHourGrid();
this.refreshBody();
}
}