UNPKG

angular-6-calendar-week-hours-view

Version:

This project supports on Angular 6.0+. and aims to provide an alternative view to those already provided on [Angular Calendar](https://github.com/mattlewis92/angular-calendar) v0.25.2. based out of [Angular Calendar Week Hours View](https://github.com/Inn

500 lines (441 loc) 15.9 kB
import { ChangeDetectorRef, Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, OnDestroy, OnInit, Output, TemplateRef } from '@angular/core'; import {CalendarEvent, DayViewHour, DayViewHourSegment, WeekDay, WeekViewEvent, WeekViewEventRow} from 'calendar-utils'; import {Subject} from 'rxjs/Subject'; import {ResizeEvent} from 'angular-resizable-element'; import {addDays} from 'date-fns'; import {Subscription} from 'rxjs/Subscription'; import { CalendarEventTimesChangedEvent, CalendarUtils } from 'angular-calendar'; import {WeekViewEventResize} from 'angular-calendar/modules/week/calendar-week-view.component'; import {validateEvents} from 'angular-calendar/modules/common/util'; import {CalendarResizeHelper} from 'angular-calendar/modules/common/calendar-resize-helper.provider'; import {CalendarDragHelper} from 'angular-calendar/modules/common/calendar-drag-helper.provider'; @Component({ selector: 'jc-calendar-week-hours-view', template: ` <div class="cal-week-hours-view" #weekViewContainer> <iq-calendar-week-hours-view-header [days]="days" [locale]="locale" [customTemplate]="headerTemplate" (dayHeaderClicked)="dayHeaderClicked.emit($event)" (eventDropped)="eventTimesChanged.emit($event)"> </iq-calendar-week-hours-view-header> <div class="cal-days-container"> <div class="cal-day-container"> <div class="cal-day-view"> <div class="cal-hour-rows"> <div class="cal-events"> <div class="cal-hour" [class.cal-week-hour-even]="i % 2 === 0" [class.cal-week-hour-odd]="i % 2 === 1" *ngFor="let hour of hours; let i = index"> <iq-calendar-week-hours-day-view-hour-segment *ngFor="let segment of hour.segments" [style.height.px]="hourSegmentHeight" [segment]="segment" [segmentHeight]="hourSegmentHeight" [locale]="locale" [customTemplate]="hourSegmentTemplate" [class.cal-drag-over]="segment.dragOver" mwlDroppable (dragEnter)="segment.dragOver = true" (dragLeave)="segment.dragOver = false" (drop)="segment.dragOver = false; eventDropped($event, segment)"> </iq-calendar-week-hours-day-view-hour-segment> </div> </div> </div> </div> </div> <div class="cal-day-container" [class.cal-past]="day.isPast" [class.cal-today]="day.isToday" [class.cal-future]="day.isFuture" *ngFor="let day of days"> <iq-calendar-week-hours-day-view [dayStartHour]="dayStartHour" [dayStartMinute]="dayStartMinute" [dayEndHour]="dayEndHour" [dayEndMinute]="dayEndMinute" [events]="events" [viewDate]="day.date" [hourSegments]="hourSegments" [hourSegmentHeight]="hourSegmentHeight" [eventWidth]="(weekViewContainer.offsetWidth / 8)" (eventClicked)="eventClicked.emit($event)" (hourSegmentClicked)="hourSegmentClicked.emit($event)" (eventTimesChanged)="eventTimesChanged.emit($event)" [eventTitleTemplate]="eventTitleTemplate" [eventTemplate]="eventTemplate"> </iq-calendar-week-hours-day-view> </div> </div> </div> ` }) export class CalendarWeekHoursViewComponent implements OnChanges, OnInit, OnDestroy { /** * The current view date */ @Input() viewDate: Date; /** * An array of events to display on view * The schema is available here: * https://github.com/mattlewis92/calendar-utils/blob/c51689985f59a271940e30bc4e2c4e1fee3fcb5c/src/calendarUtils.ts#L49-L63 */ @Input() events: CalendarEvent[] = []; /** * An array of day indexes (0 = sunday, 1 = monday etc) that will be hidden on the view */ @Input() excludeDays: number[] = []; /** * An observable that when emitted on will re-render the current view */ @Input() refresh: Subject<any>; /** * The locale used to format dates */ @Input() locale: string; /** * The placement of the event tooltip */ @Input() tooltipPlacement = 'bottom'; /** * A custom template to use for the event tooltips */ @Input() tooltipTemplate: TemplateRef<any>; /** * Whether to append tooltips to the body or next to the trigger element */ @Input() tooltipAppendToBody = true; /** * The start number of the week */ @Input() weekStartsOn: number; /** * A custom template to use to replace the header */ @Input() headerTemplate: TemplateRef<any>; /** * A custom template to use for week view events */ @Input() eventTemplate: TemplateRef<any>; /** * A custom template to use for event titles */ @Input() eventTitleTemplate: 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 */ @Input() precision: 'days' | 'minutes' = 'days'; /** * An array of day indexes (0 = sunday, 1 = monday etc) that indicate which days are weekends */ @Input() weekendDays: number[]; /** * The day start hours in 24 hour time. Must be 0-23 */ @Input() dayStartHour = 0; /** * The day start minutes. Must be 0-59 */ @Input() dayStartMinute = 0; /** * The day end hours in 24 hour time. Must be 0-23 */ @Input() dayEndHour = 23; /** * The day end minutes. Must be 0-59 */ @Input() dayEndMinute = 59; /** * The number of segments in an hour. Must be <= 6 */ @Input() hourSegments = 2; /** * The height in pixels of each hour segment */ @Input() hourSegmentHeight = 30; /** * A custom template to use to replace the hour segment */ @Input() hourSegmentTemplate: TemplateRef<any>; /** * Called when a header week day is clicked. * Adding a `cssClass` property on `$event.day` will add that class to the header element */ @Output() dayHeaderClicked: EventEmitter<{ day: WeekDay }> = new EventEmitter<{ day: WeekDay; }>(); /** * Called when the event title is clicked */ @Output() eventClicked: EventEmitter<{ event: CalendarEvent }> = new EventEmitter<{ event: CalendarEvent; }>(); /** * Called when an hour segment is clicked */ @Output() hourSegmentClicked: EventEmitter<{ date: Date }> = new EventEmitter<{ date: Date; }>(); /** * Called when an event is resized or dragged and dropped */ @Output() eventTimesChanged: EventEmitter<CalendarEventTimesChangedEvent> = new EventEmitter<CalendarEventTimesChangedEvent>(); /** * An output that will be called before the view is rendered for the current week. * If you add the `cssClass` property to a day in the header it will add that class to the cell element in the template */ @Output() beforeViewRender: EventEmitter<{ header: WeekDay[] }> = new EventEmitter(); /** * @hidden */ hours: DayViewHour[] = []; /** * @hidden */ days: WeekDay[]; /** * @hidden */ eventRows: WeekViewEventRow[] = []; /** * @hidden */ refreshSubscription: Subscription; /** * @hidden */ currentResizes: Map<WeekViewEvent, WeekViewEventResize> = new Map(); /** * @hidden */ validateDrag: (args: any) => boolean; /** * @hidden */ validateResize: (args: any) => boolean; /** * @hidden */ dayColumnWidth: number; /** * @hidden */ constructor(private cdr: ChangeDetectorRef, private utils: CalendarUtils, @Inject(LOCALE_ID) locale: string) { this.locale = locale; } /** * @hidden */ ngOnInit(): void { if (this.refresh) { this.refreshSubscription = this.refresh.subscribe(() => { this.refreshAll(); this.cdr.markForCheck(); }); } } /** * @hidden */ ngOnChanges(changes: any): void { if (changes.viewDate || changes.excludeDays || changes.weekendDays) { this.refreshHeader(); } if (changes.events) { validateEvents(this.events); } 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(); } } /** * @hidden */ resizeStarted(weekViewContainer: HTMLElement, weekEvent: WeekViewEvent, resizeEvent: ResizeEvent): void { this.currentResizes.set(weekEvent, { originalOffset: weekEvent.offset, originalSpan: weekEvent.span, edge: typeof resizeEvent.edges.left !== 'undefined' ? 'left' : 'right' }); this.dayColumnWidth = this.getDayColumnWidth(weekViewContainer); const resizeHelper: CalendarResizeHelper = new CalendarResizeHelper( weekViewContainer, this.dayColumnWidth ); this.validateResize = ({rectangle}) => resizeHelper.validateResize({rectangle}); this.cdr.markForCheck(); } /** * @hidden */ resizing(weekEvent: WeekViewEvent, resizeEvent: ResizeEvent, dayWidth: number): void { const currentResize: WeekViewEventResize = this.currentResizes.get( weekEvent ); if (resizeEvent.edges.left) { const diff: number = Math.round(+resizeEvent.edges.left / dayWidth); weekEvent.offset = currentResize.originalOffset + diff; weekEvent.span = currentResize.originalSpan - diff; } else if (resizeEvent.edges.right) { const diff: number = Math.round(+resizeEvent.edges.right / dayWidth); weekEvent.span = currentResize.originalSpan + diff; } } /** * @hidden */ resizeEnded(weekEvent: WeekViewEvent): void { const currentResize: WeekViewEventResize = this.currentResizes.get( weekEvent ); let daysDiff: number; if (currentResize.edge === 'left') { daysDiff = weekEvent.offset - currentResize.originalOffset; } else { daysDiff = weekEvent.span - currentResize.originalSpan; } weekEvent.offset = currentResize.originalOffset; weekEvent.span = currentResize.originalSpan; let newStart: Date = weekEvent.event.start; let newEnd: Date = weekEvent.event.end; if (currentResize.edge === 'left') { newStart = addDays(newStart, daysDiff); } else if (newEnd) { newEnd = addDays(newEnd, daysDiff); } this.eventTimesChanged.emit({newStart, newEnd, event: weekEvent.event}); this.currentResizes.delete(weekEvent); } /** * @hidden */ eventDragged(weekEvent: WeekViewEvent, draggedByPx: number, dayWidth: number): void { const daysDragged: number = draggedByPx / dayWidth; // TODO - remove this check once https://github.com/mattlewis92/angular-draggable-droppable/issues/21 is fixed if (daysDragged !== 0) { 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}); } } /** * @hidden */ getDayColumnWidth(eventRowContainer: HTMLElement): number { return Math.floor(eventRowContainer.offsetWidth / this.days.length); } /** * @hidden */ dragStart(weekViewContainer: HTMLElement, event: HTMLElement): void { this.dayColumnWidth = this.getDayColumnWidth(weekViewContainer); const dragHelper: CalendarDragHelper = new CalendarDragHelper( weekViewContainer, event ); this.validateDrag = ({x, y}) => this.currentResizes.size === 0 && dragHelper.validateDrag({x, y}); this.cdr.markForCheck(); } private refreshHeader(): void { this.days = this.utils.getWeekViewHeader({ viewDate: this.viewDate, weekStartsOn: this.weekStartsOn, excluded: this.excludeDays, weekendDays: this.weekendDays }); this.beforeViewRender.emit({ header: this.days }); } private refreshBody(): void { this.eventRows = this.utils.getWeekView({ events: this.events, viewDate: this.viewDate, weekStartsOn: this.weekStartsOn, excluded: this.excludeDays, precision: this.precision, absolutePositionedEvents: true }).eventRows; } private refreshHourGrid(): void { this.hours = this.utils.getDayViewHourGrid({ viewDate: this.viewDate, hourSegments: this.hourSegments, dayStart: { hour: this.dayStartHour, minute: this.dayStartMinute }, dayEnd: { hour: this.dayEndHour, minute: this.dayEndMinute } }); } private refreshAll(): void { this.refreshHeader(); this.refreshBody(); this.refreshHourGrid(); } eventDropped(dropEvent: { dropData?: { event?: CalendarEvent } }, segment: DayViewHourSegment): void { if (dropEvent.dropData && dropEvent.dropData.event) { this.eventTimesChanged.emit({ event: dropEvent.dropData.event, newStart: segment.date }); } } }