UNPKG

angular-calendar-scheduler

Version:

This project provide a scheduler view component for [mattlewis92/angular-calendar](https://github.com/mattlewis92/angular-calendar).

831 lines (825 loc) 145 kB
import * as tslib_1 from "tslib"; import { Component, Input, Output, EventEmitter, ChangeDetectorRef, LOCALE_ID, Inject, TemplateRef, ViewEncapsulation } from '@angular/core'; import { Subject } from 'rxjs'; import { isBefore } from 'date-fns'; import { CalendarDragHelper } from 'angular-calendar/esm2015/modules/common/calendar-drag-helper.provider'; import { CalendarResizeHelper } from 'angular-calendar/esm2015/modules/common/calendar-resize-helper.provider'; import { SchedulerConfig } from './scheduler-config'; import { CalendarEventTimesChangedEventType, DateAdapter } from 'angular-calendar'; import { shouldFireDroppedEvent, isDraggedWithinPeriod, roundToNearest, getMinutesMoved, trackByHourColumn, trackByDayOrEvent, trackByHour, trackByHourSegment, getMinimumEventHeightInMinutes, getDefaultEventEnd } from '../common/utils'; import { DEFAULT_HOUR_SEGMENTS, DEFAULT_HOUR_SEGMENT_HEIGHT_PX, DEFAULT_EVENT_WIDTH_PERCENT, MINUTES_IN_HOUR } from './utils/calendar-scheduler-utils'; import { CalendarSchedulerUtils } from './utils/calendar-scheduler-utils.provider'; /** * [ngClass]="getPositioningClasses(event)" * * [style.top.px]="event.top" * [style.height.px]="event.height" * [style.left.%]="event.left" * [style.width.%]="event.width" * * DRAG & DROP & RESIZE -> https://github.com/mattlewis92/angular-calendar/blob/master/projects/angular-calendar/src/modules/week/calendar-week-view.component.ts * FLEXBOX -> https://css-tricks.com/snippets/css/a-guide-to-flexbox/ */ let CalendarSchedulerViewComponent = class CalendarSchedulerViewComponent { /** * @hidden */ constructor(cdr, locale, config, utils, dateAdapter) { this.cdr = cdr; this.config = config; this.utils = utils; this.dateAdapter = dateAdapter; /** * An array of events to display on view */ this.events = []; /** * The number of segments in an hour. Must be one of 1, 2, 4, 6 */ this.hourSegments = DEFAULT_HOUR_SEGMENTS; /** * The height in pixels of each hour segment */ this.hourSegmentHeight = DEFAULT_HOUR_SEGMENT_HEIGHT_PX; /** * An array of day indexes (0 = sunday, 1 = monday etc) that will be hidden on the view */ this.excludeDays = []; /** * Specify if the first day of current scheduler view has to be today or the first day of the week */ this.startsWithToday = false; /** * Specify if content must be shown or not */ this.showEventContent = true; /** * Specify if actions must be shown or not */ this.showEventActions = true; /** * Specify if status must be shown or not */ this.showEventStatus = true; /** * Specify if hour must be shown on segment or not */ this.showSegmentHour = false; /** * The grid size to snap resizing and dragging of events to */ this.eventSnapSize = this.hourSegmentHeight; /** * Whether to snap events to a grid when dragging */ this.snapDraggedEvents = true; /** * The day start hours in 24 hour time. Must be 0-23 */ this.dayStartHour = 0; /** * The day start minutes. Must be 0-59 */ this.dayStartMinute = 0; /** * The day end hours in 24 hour time. Must be 0-23 */ this.dayEndHour = 23; /** * The day end minutes. Must be 0-59 */ this.dayEndMinute = 59; /** * The width in pixels of each event on the view */ this.eventWidthPercent = DEFAULT_EVENT_WIDTH_PERCENT; /** * Called when a header week day is clicked */ this.dayHeaderClicked = new EventEmitter(); /** * Called when the hour is clicked */ this.hourClicked = new EventEmitter(); /** * Called when the segment is clicked */ this.segmentClicked = new EventEmitter(); /** * Called when the event is clicked */ this.eventClicked = new EventEmitter(); /** * Called when an event is resized or dragged and dropped */ this.eventTimesChanged = new EventEmitter(); /** * @hidden */ this.hours = []; /** * @hidden */ // resizes: Map<CalendarSchedulerEvent, SchedulerResizeEvent> = new Map(); this.resizes = new Map(); /** * @hidden */ this.eventDragEnter = 0; /** * @hidden */ this.dragActive = false; /** * @hidden */ this.calendarId = Symbol('angular calendar scheduler view id'); /** * @hidden */ this.trackByHourColumn = trackByHourColumn; /** * @hidden */ this.trackByDayOrEvent = trackByDayOrEvent; /** * @hidden */ this.trackByHour = trackByHour; /** * @hidden */ this.trackByHourSegment = trackByHourSegment; this.locale = this.config.locale || locale; } /** * @hidden */ ngOnInit() { if (this.refresh) { this.refreshSubscription = this.refresh.subscribe(() => { this.refreshAll(); this.cdr.markForCheck(); }); } } /** * @hidden */ ngOnChanges(changes) { if (changes.viewDate || changes.excludeDays || changes.weekendDays) { this.refreshHeader(); } if (changes.viewDate || changes.events || changes.dayStartHour || changes.dayEndHour || changes.dayStartMinute || changes.dayEndMinute || changes.excludeDays || changes.eventWidth) { this.refreshHourGrid(); this.refreshBody(); } } /** * @hidden */ ngOnDestroy() { if (this.refreshSubscription) { this.refreshSubscription.unsubscribe(); } } getPositioningClasses(day, event) { const classes = [ this.getDayClass(event.start), this.getTimeClass(day.date, event), this.getLengthClass(day.date, event) ]; return classes.join(' '); } getDayClass(date) { return ''; } getTimeClass(date, event) { if (this.dateAdapter.isSameDay(date, event.start)) { let hours = event.start.getHours(); if (this.dayStartHour > 0) { hours = event.start.getHours() - this.dayStartHour; } const hoursString = hours < 10 ? `0${hours}` : `${hours}`; const minutesString = event.start.getMinutes() < 10 ? `0${event.start.getMinutes()}` : `${event.start.getMinutes()}`; return `time${hoursString}${minutesString}`; } else if (isBefore(event.start, this.dateAdapter.startOfDay(date))) { return `time0000`; } } getLengthClass(date, event) { if (this.dateAdapter.isSameDay(date, event.start)) { const durationInMinutes = this.dateAdapter.differenceInMinutes(event.end, event.start); const leftToEndOfDay = this.dateAdapter.differenceInMinutes(this.dateAdapter.setMinutes(this.dateAdapter.setHours(event.start, this.dayEndHour + 1), 0), event.start); return leftToEndOfDay > durationInMinutes ? `length${durationInMinutes}` : `length${leftToEndOfDay}`; } else if (isBefore(event.start, this.dateAdapter.startOfDay(date))) { let leftDurationInMinutes = 0; if (this.dateAdapter.isSameDay(date, event.end)) { leftDurationInMinutes = this.dateAdapter.differenceInMinutes(event.end, this.dateAdapter.startOfDay(date)); if (this.dayStartHour > 0) { leftDurationInMinutes = (event.end.getHours() - this.dayStartHour) * MINUTES_IN_HOUR; } } else { leftDurationInMinutes = ((this.dayEndHour + 1) - this.dayStartHour) * MINUTES_IN_HOUR; } return `length${leftDurationInMinutes}`; } } refreshHourGrid() { this.hours = this.utils.getSchedulerViewHourGrid({ viewDate: this.viewDate, hourSegments: this.hourSegments, dayStart: { hour: this.dayStartHour, minute: this.dayStartMinute }, dayEnd: { hour: this.dayEndHour, minute: this.dayEndMinute } }); } refreshHeader() { this.days = this.utils.getSchedulerViewDays({ viewDate: this.viewDate, weekStartsOn: this.weekStartsOn, startsWithToday: this.startsWithToday, excluded: this.excludeDays, weekendDays: this.weekendDays }); } refreshBody(events) { this.view = this.getSchedulerView(events || this.events); this.view.days.forEach((day) => { day.events.forEach((event) => { this.scaleOverlappingEvents(event.event.start, event.event.end, day.events); }); }); if (this.dayModifier) { this.days.forEach(day => this.dayModifier(day)); } if (this.dayModifier || this.hourModifier || this.segmentModifier) { this.view.days.forEach(day => { if (this.dayModifier) { this.dayModifier(day); } day.hours.forEach((hour) => { if (this.hourModifier) { this.hourModifier(hour); } hour.segments.forEach((segment) => { if (this.segmentModifier) { this.segmentModifier(segment); } }); }); }); } if (this.eventModifier) { this.events.forEach(event => this.eventModifier(event)); } } refreshAll() { this.refreshHeader(); this.refreshHourGrid(); this.refreshBody(); } getSchedulerView(events) { return this.utils.getSchedulerView({ events: events, viewDate: this.viewDate, hourSegments: this.hourSegments, weekStartsOn: this.weekStartsOn, startsWithToday: this.startsWithToday, dayStart: { hour: this.dayStartHour, minute: this.dayStartMinute }, dayEnd: { hour: this.dayEndHour, minute: this.dayEndMinute }, excluded: this.excludeDays, eventWidth: this.eventWidthPercent, hourSegmentHeight: this.hourSegmentHeight }); } scaleOverlappingEvents(startTime, endTime, events) { let newStartTime = startTime; let newEndTime = endTime; const overlappingEvents = []; let maxLeft = 0; events.forEach((event) => { if (event.isProcessed) { return; } if (event.event.start < startTime && event.event.end > startTime) { newStartTime = event.event.start; } else if (event.event.end > endTime && event.event.start < endTime) { newEndTime = event.event.end; } else if (event.event.end <= endTime && event.event.start >= startTime) { // Nothing, but remove condition and add equals to above two for overlapping effect } else { return; } if (event.left > maxLeft) { maxLeft = event.left; } overlappingEvents.push(event); }); if (startTime === newStartTime && endTime === newEndTime) { const divisorFactor = Math.floor(maxLeft / this.eventWidthPercent) + 1; overlappingEvents.forEach((event) => { event.isProcessed = true; event.left /= divisorFactor; event.width /= divisorFactor; }); } else { this.scaleOverlappingEvents(newStartTime, newEndTime, events); } } //#region RESIZE /** * @hidden */ resizeStarted(eventsContainer, event, resizeEvent) { this.resizes.set(event.event, resizeEvent); this.dayColumnWidth = Math.floor(eventsContainer.offsetWidth / this.days.length); const resizeHelper = new CalendarResizeHelper(eventsContainer); this.validateResize = ({ rectangle }) => resizeHelper.validateResize({ rectangle }); this.cdr.markForCheck(); } /** * @hidden */ resizing(event, resizeEvent) { this.resizes.set(event.event, resizeEvent); const adjustedEvents = new Map(); const tempEvents = [...this.events]; this.resizes.forEach((lastResizeEvent, ev) => { const newEventDates = this.getResizedEventDates(ev, lastResizeEvent); const adjustedEvent = Object.assign({}, ev, newEventDates); adjustedEvents.set(adjustedEvent, ev); const eventIndex = tempEvents.indexOf(ev); tempEvents[eventIndex] = adjustedEvent; }); this.restoreOriginalEvents(tempEvents, adjustedEvents); } /** * @hidden */ resizeEnded(event) { this.view = this.getSchedulerView(this.events); const lastResizeEvent = this.resizes.get(event.event); this.resizes.delete(event.event); const newEventDates = this.getResizedEventDates(event.event, lastResizeEvent); this.eventTimesChanged.emit({ newStart: newEventDates.start, newEnd: newEventDates.end, event: event.event, type: CalendarEventTimesChangedEventType.Resize }); } getResizedEventDates(event, resizeEvent) { const minimumEventHeight = getMinimumEventHeightInMinutes(this.hourSegments, this.hourSegmentHeight); const newEventDates = { start: event.start, end: getDefaultEventEnd(this.dateAdapter, event, minimumEventHeight) }; const { end } = event, eventWithoutEnd = tslib_1.__rest(event, ["end"]); const smallestResizes = { start: this.dateAdapter.addMinutes(newEventDates.end, minimumEventHeight * -1), end: getDefaultEventEnd(this.dateAdapter, eventWithoutEnd, minimumEventHeight) }; if (resizeEvent.edges.left) { const daysDiff = Math.round(+resizeEvent.edges.left / this.dayColumnWidth); const newStart = this.dateAdapter.addDays(newEventDates.start, daysDiff); if (newStart < smallestResizes.start) { newEventDates.start = newStart; } else { newEventDates.start = smallestResizes.start; } } else if (resizeEvent.edges.right) { const daysDiff = Math.round(+resizeEvent.edges.right / this.dayColumnWidth); const newEnd = this.dateAdapter.addDays(newEventDates.end, daysDiff); if (newEnd > smallestResizes.end) { newEventDates.end = newEnd; } else { newEventDates.end = smallestResizes.end; } } if (resizeEvent.edges.top) { const precision = this.eventSnapSize || this.hourSegmentHeight; const draggedInPixelsSnapSize = Math.round(resizeEvent.edges.top / precision) * precision; const pixelAmountInMinutes = MINUTES_IN_HOUR / (this.hourSegments * this.hourSegmentHeight); const minutesMoved = draggedInPixelsSnapSize * pixelAmountInMinutes; const newStart = this.dateAdapter.addMinutes(newEventDates.start, minutesMoved); if (newStart < smallestResizes.start) { newEventDates.start = newStart; } else { newEventDates.start = smallestResizes.start; } } else if (resizeEvent.edges.bottom) { const precision = this.eventSnapSize || this.hourSegmentHeight; const draggedInPixelsSnapSize = Math.round(resizeEvent.edges.bottom / precision) * precision; const pixelAmountInMinutes = MINUTES_IN_HOUR / (this.hourSegments * this.hourSegmentHeight); const minutesMoved = draggedInPixelsSnapSize * pixelAmountInMinutes; const newEnd = this.dateAdapter.addMinutes(newEventDates.end, minutesMoved); if (newEnd > smallestResizes.end) { newEventDates.end = newEnd; } else { newEventDates.end = smallestResizes.end; } } return newEventDates; } //#endregion //#region DRAG & DROP /** * @hidden */ eventDropped(dropEvent, date) { if (shouldFireDroppedEvent(dropEvent, date, this.calendarId)) { this.eventTimesChanged.emit({ type: CalendarEventTimesChangedEventType.Drop, event: dropEvent.dropData.event, newStart: date, newEnd: null }); } } /** * @hidden */ dragStarted(eventsContainer, eventContainer, event) { this.dayColumnWidth = Math.floor(eventsContainer.offsetWidth / this.days.length); const dragHelper = new CalendarDragHelper(eventsContainer, eventContainer); this.validateDrag = ({ x, y }) => this.resizes.size === 0 && dragHelper.validateDrag({ x, y, snapDraggedEvents: this.snapDraggedEvents }); this.dragActive = true; this.eventDragEnter = 0; if (!this.snapDraggedEvents && event) { this.view.days.forEach((day) => { const linkedEvent = day.events.find(ev => ev.event === event.event && ev !== event); // hide any linked events while dragging if (linkedEvent) { linkedEvent.width = 0; linkedEvent.height = 0; } }); } this.cdr.markForCheck(); } /** * @hidden */ dragMove(event, dragEvent) { if (this.snapDraggedEvents) { const newEventTimes = this.getDragMovedEventTimes(event, dragEvent, this.dayColumnWidth, true); const originalEvent = event.event; const adjustedEvent = Object.assign({}, originalEvent, newEventTimes); const tempEvents = this.events.map(ev => { if (ev === originalEvent) { return adjustedEvent; } return ev; }); this.restoreOriginalEvents(tempEvents, new Map([[adjustedEvent, originalEvent]])); } } dragEnded(event, dragEndEvent, dayWidth, useY = false) { this.view = this.getSchedulerView(this.events); this.dragActive = false; const { start, end } = this.getDragMovedEventTimes(event, dragEndEvent, dayWidth, useY); if (this.eventDragEnter > 0 && isDraggedWithinPeriod(start, end, this.view.period)) { this.eventTimesChanged.emit({ newStart: start, newEnd: end, event: event.event, type: CalendarEventTimesChangedEventType.Drag }); } } getDragMovedEventTimes(event, dragEndEvent, dayWidth, useY) { const daysDragged = roundToNearest(dragEndEvent.x, dayWidth) / dayWidth; const minutesMoved = useY ? getMinutesMoved(dragEndEvent.y, this.hourSegments, this.hourSegmentHeight, this.eventSnapSize) : 0; const start = this.dateAdapter.addMinutes(this.dateAdapter.addDays(event.event.start, daysDragged), minutesMoved); let end; if (event.event.end) { end = this.dateAdapter.addMinutes(this.dateAdapter.addDays(event.event.end, daysDragged), minutesMoved); } return { start, end }; } restoreOriginalEvents(tempEvents, adjustedEvents) { this.refreshBody(tempEvents); const adjustedEventsArray = tempEvents.filter(event => adjustedEvents.has(event)); this.view.days.forEach(day => { adjustedEventsArray.forEach(adjustedEvent => { const originalEvent = adjustedEvents.get(adjustedEvent); const existingColumnEvent = day.events.find(ev => ev.event === adjustedEvent); if (existingColumnEvent) { // restore the original event so trackBy kicks in and the dom isn't changed existingColumnEvent.event = originalEvent; } else { // add a dummy event to the drop so if the event was removed from the original column the drag doesn't end early day.events.push({ event: originalEvent, left: 0, top: 0, height: 0, width: 0, startsBeforeDay: false, endsAfterDay: false }); } }); }); adjustedEvents.clear(); } }; tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Date) ], CalendarSchedulerViewComponent.prototype, "viewDate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Array) ], CalendarSchedulerViewComponent.prototype, "events", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "hourSegments", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "hourSegmentHeight", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Array) ], CalendarSchedulerViewComponent.prototype, "excludeDays", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "startsWithToday", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "showEventContent", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "showEventActions", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "showEventStatus", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "showSegmentHour", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Function) ], CalendarSchedulerViewComponent.prototype, "dayModifier", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Function) ], CalendarSchedulerViewComponent.prototype, "hourModifier", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Function) ], CalendarSchedulerViewComponent.prototype, "segmentModifier", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Function) ], CalendarSchedulerViewComponent.prototype, "eventModifier", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Subject) ], CalendarSchedulerViewComponent.prototype, "refresh", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", String) ], CalendarSchedulerViewComponent.prototype, "locale", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "eventSnapSize", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Boolean) ], CalendarSchedulerViewComponent.prototype, "snapDraggedEvents", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "weekStartsOn", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", TemplateRef) ], CalendarSchedulerViewComponent.prototype, "headerTemplate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", TemplateRef) ], CalendarSchedulerViewComponent.prototype, "cellTemplate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", TemplateRef) ], CalendarSchedulerViewComponent.prototype, "eventTemplate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", TemplateRef) ], CalendarSchedulerViewComponent.prototype, "eventTitleTemplate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", TemplateRef) ], CalendarSchedulerViewComponent.prototype, "allDayEventTemplate", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Array) ], CalendarSchedulerViewComponent.prototype, "weekendDays", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "dayStartHour", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "dayStartMinute", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "dayEndHour", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "dayEndMinute", void 0); tslib_1.__decorate([ Input(), tslib_1.__metadata("design:type", Number) ], CalendarSchedulerViewComponent.prototype, "eventWidthPercent", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], CalendarSchedulerViewComponent.prototype, "dayHeaderClicked", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], CalendarSchedulerViewComponent.prototype, "hourClicked", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], CalendarSchedulerViewComponent.prototype, "segmentClicked", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], CalendarSchedulerViewComponent.prototype, "eventClicked", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], CalendarSchedulerViewComponent.prototype, "eventTimesChanged", void 0); CalendarSchedulerViewComponent = tslib_1.__decorate([ Component({ selector: 'calendar-scheduler-view', template: ` <div class="cal-scheduler-view"> <calendar-scheduler-header [days]="days" [locale]="locale" [customTemplate]="headerTemplate" (dayHeaderClicked)="dayHeaderClicked.emit($event)"> </calendar-scheduler-header> <div class="cal-scheduler" #calendarContainer> <div class="cal-scheduler-hour-rows aside"> <div class="cal-scheduler-hour align-center horizontal" *ngFor="let hour of hours; trackBy:trackByHour"> <div class="cal-scheduler-time"> <div class="cal-scheduler-time-segment" *ngFor="let segment of hour.segments" [style.height.px]="hourSegmentHeight"> {{ segment.date | calendarDate:'dayViewHour':locale }} </div> </div> </div> </div> <div class="cal-scheduler-cols aside" #dayColumns [class.cal-resize-active]="resizes.size > 0" mwlDroppable (dragEnter)="eventDragEnter = eventDragEnter + 1" (dragLeave)="eventDragEnter = eventDragEnter - 1"> <div class="cal-scheduler-col" *ngFor="let day of view.days; trackBy:trackByHourColumn" [ngClass]="day?.cssClass" [style.backgroundColor]="day.backgroundColor"> <div #eventContainer class="cal-scheduler-event-container" *ngFor="let event of day.events; trackBy:trackByDayOrEvent" [ngClass]="event.event?.cssClass" [hidden]="event.height === 0 && event.width === 0" [style.top.px]="event.top" [style.height.px]="event.height" [style.left.%]="event.left" [style.width.%]="event.width" mwlResizable [resizeSnapGrid]="{left: dayColumnWidth, right: dayColumnWidth, top: eventSnapSize || hourSegmentHeight, bottom: eventSnapSize || hourSegmentHeight}" [validateResize]="validateResize" [allowNegativeResizes]="true" (resizeStart)="resizeStarted(dayColumns, event, $event)" (resizing)="resizing(event, $event)" (resizeEnd)="resizeEnded(event)" mwlDraggable dragActiveClass="cal-drag-active" [dropData]="{event: event.event, calendarId: calendarId}" [dragAxis]="{ x: event.event.draggable && resizes.size === 0, y: event.event.draggable && resizes.size === 0 }" [dragSnapGrid]="snapDraggedEvents ? {x: dayColumnWidth, y: eventSnapSize || hourSegmentHeight} : {}" [ghostDragEnabled]="!snapDraggedEvents" [validateDrag]="validateDrag" (dragPointerDown)="dragStarted(dayColumns, eventContainer, event)" (dragging)="dragMove(event, $event)" (dragEnd)="dragEnded(event, $event, dayColumnWidth, true)"> <div *ngIf="event.event?.resizable?.beforeStart && !event.startsBeforeDay" class="cal-resize-handle cal-resize-handle-before-start" mwlResizeHandle [resizeEdges]="{ left: true, top: true }"> </div> <calendar-scheduler-event [day]="day" [event]="event" [showContent]="showEventContent && event.height >= 75" [showActions]="showEventActions" [showStatus]="showEventStatus" [customTemplate]="eventTemplate" [eventTitleTemplate]="eventTitleTemplate" (eventClicked)="eventClicked.emit($event)"> </calendar-scheduler-event> <div *ngIf="event.event?.resizable?.afterEnd && !event.endsAfterDay" class="cal-resize-handle cal-resize-handle-after-end" mwlResizeHandle [resizeEdges]="{ right: true, bottom: true }"> </div> </div> <div class="cal-scheduler-hour" *ngFor="let hour of day.hours; let i = index; trackBy:trackByHour" [class.cal-even]="i % 2 === 0" [class.cal-odd]="i % 2 === 1" [ngClass]="hour.cssClass" [style.backgroundColor]="hour.backgroundColor" (mwlClick)="hourClicked.emit({hour: hour})" [class.cal-past]="day.isPast" [class.cal-today]="day.isToday" [class.cal-future]="day.isFuture" [class.cal-weekend]="day.isWeekend" [class.cal-in-month]="day.inMonth" [class.cal-out-month]="!day.inMonth"> <div class="cal-scheduler-hour-segments"> <calendar-scheduler-hour-segment *ngFor="let segment of hour.segments; trackBy:trackByHourSegment" [day]="day" [segment]="segment" [locale]="locale" [customTemplate]="cellTemplate" [hourSegmentHeight]="hourSegmentHeight" [showHour]="showSegmentHour" (segmentClicked)="segmentClicked.emit($event)" mwlDroppable [dragOverClass]="!dragActive || !snapDraggedEvents ? 'cal-drag-over' : 'null'" dragActiveClass="cal-drag-active" (drop)="eventDropped($event, segment.date)"> </calendar-scheduler-hour-segment> </div> </div> </div> </div> </div> </div> `, encapsulation: ViewEncapsulation.None, styles: [".cal-scheduler-view *{box-sizing:border-box}.cal-scheduler-view .cal-scheduler-headers{display:flex;flex-flow:row wrap;margin-bottom:3px;border:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler-headers .aside{flex:1 0}.cal-scheduler-view .cal-scheduler-headers .aside.cal-header-clock{max-width:5em}.cal-scheduler-view .cal-scheduler-headers .cal-header{flex:1;text-align:center;padding:5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default}.cal-scheduler-view .cal-scheduler-headers .cal-header:not(:last-child){border-right:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler-headers .cal-header:hover{background-color:#ededed}.cal-scheduler-view .cal-scheduler-headers .cal-header.cal-today{background-color:#e8fde7}.cal-scheduler-view .cal-scheduler-headers .cal-header.cal-weekend span{color:#8b0000}.cal-scheduler-view .cal-scheduler-headers .cal-header span{font-weight:400;opacity:.5}.cal-scheduler-view .cal-scheduler,.cal-scheduler-view .cal-scheduler-headers .cal-header-cols{display:flex;flex-flow:row wrap}.cal-scheduler-view .cal-scheduler .aside{flex:1 0}.cal-scheduler-view .cal-scheduler .aside.cal-scheduler-hour-rows{max-width:5em}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows{width:auto!important;border:1px solid #e1e1e1;overflow:hidden;position:relative}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour{display:flex}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour:nth-child(odd){background-color:#fafafa}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour:not(:last-child){border-bottom:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour .cal-scheduler-time{display:flex;flex-flow:column wrap;width:100%;font-weight:700;text-align:center}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour .cal-scheduler-time .cal-scheduler-time-segment{cursor:default}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour .cal-scheduler-time .cal-scheduler-time-segment:hover{background-color:#ededed}.cal-scheduler-view .cal-scheduler .cal-scheduler-hour-rows .cal-scheduler-hour .cal-scheduler-time .cal-scheduler-time-segment:not(:last-child){border-bottom:thin dashed #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols{display:flex;flex-flow:row wrap;border-top:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols:not(.cal-resize-active) .cal-scheduler-hour-segment:hover{background-color:#ededed}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col{display:flex;flex-flow:column wrap;flex:1 0;position:relative;border-right:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour{display:flex;flex-flow:column wrap;flex:1 0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour.cal-today{background-color:#e8fde7}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour.cal-disabled{background-color:#eee;pointer-events:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour.cal-odd{background:#fff}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour.cal-even{background:#fafafa}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments{display:flex;flex-flow:column wrap;flex:1 0;border-bottom:1px solid #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments.no-border{border-bottom:0!important}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments.cal-disabled{background-color:#eee;pointer-events:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments.cal-disabled .cal-scheduler-event{filter:opacity(50%);-webkit-filter:opacity(50%)}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments :not(:last-child) .cal-scheduler-hour-segment{border-bottom:thin dashed #e1e1e1}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments .cal-scheduler-hour-segment{flex:1 0;display:flex;flex-flow:column wrap}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments .cal-scheduler-hour-segment.no-border{border-bottom:0!important}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments .cal-scheduler-hour-segment.cal-disabled{background-color:#eee;pointer-events:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments .cal-scheduler-hour-segment.cal-drag-over{background-color:#ededed}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-hour .cal-scheduler-hour-segments .cal-scheduler-hour-segment .cal-scheduler-time{font-weight:700;font-size:.7em;color:#6c757d}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container{flex:1 0;display:-ms-flexbox;flex-flow:column wrap;position:absolute}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event{flex:1 0;display:flex;flex-flow:column wrap;padding:0 10px;font-size:12px;margin:2px;line-height:30px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:.3em;border:1px solid #1e90ff;background-color:#d1e8ff;height:calc(100% - 5px);transition:all ease-out .2s;filter:brightness(100%);-webkit-filter:brightness(100%);-webkit-backface-visibility:hidden;backface-visibility:hidden}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event.cal-cancelled,.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event.cal-disabled{background-color:gray!important;filter:grayscale(100%);-webkit-filter:grayscale(100%)}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event.cal-not-clickable{cursor:not-allowed!important}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event:not(.cal-disabled):not(.cal-cancelled):hover{cursor:pointer;filter:brightness(95%);-webkit-filter:brightness(95%)}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event:not(.cal-disabled):not(.cal-cancelled).cal-draggable{cursor:move}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event.cal-starts-before-day{border-top-left-radius:0;border-top-right-radius:0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event.cal-ends-after-day{border-bottom-left-radius:0;border-bottom-right-radius:0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container{width:100%;display:flex;flex-flow:row wrap}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container .cal-scheduler-event-title{flex:1 0;font-size:16px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container .cal-scheduler-event-status{margin:7px 0;width:16px;height:16px;background:grey;border-radius:50px;border:1px solid #000}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container .cal-scheduler-event-status.ok{background:green}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container .cal-scheduler-event-status.warning{background:orange}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-title-container .cal-scheduler-event-status.danger{background:red}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-content-container{flex:1 auto;width:100%;padding-right:1.5em}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-content-container .cal-scheduler-event-content{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-actions-container{width:100%;position:relative}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-actions-container.no-content-actions{flex:1 0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-actions-container:not(.no-content-actions) .cal-scheduler-event-actions{position:absolute;bottom:5px;right:0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-scheduler-event .cal-scheduler-event-actions-container .cal-scheduler-event-actions .cal-scheduler-event-action{text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-resize-handle{width:100%;height:4px;cursor:row-resize;position:absolute}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container .cal-resize-handle.cal-resize-handle-after-end{bottom:0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.cal-drag-active{pointer-events:none;z-index:999}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.cal-drag-active *{pointer-events:none}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day1{left:5%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day2{left:10%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day3{left:15%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day4{left:20%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day5{left:25%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day6{left:30%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.day7{left:35%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0000{top:0}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0015{top:1.47%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0030{top:2.94%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0045{top:4.41%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0100{top:5.88%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0115{top:7.35%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0130{top:8.82%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0145{top:10.29%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0200{top:11.76%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0215{top:13.23%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0230{top:14.7%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0245{top:16.17%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0300{top:17.64%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0315{top:19.11%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0330{top:20.58%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0345{top:22.05%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0400{top:23.52%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0415{top:24.99%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0430{top:26.46%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0445{top:27.93%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0500{top:29.4%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time0515{top:30.87%}.cal-scheduler-view .cal-scheduler .cal-scheduler-cols .cal-scheduler-col .cal-scheduler-event-container.time05