Skip to content

Commit

Permalink
feat(day-view): allow events to be dragged outside of the view
Browse files Browse the repository at this point in the history
BREAKING CHANGE: if you were extending the day view component then the internal API has changed slightly and you may need to adjust your app

Closes #532
  • Loading branch information
mattlewis92 committed Jun 18, 2018
1 parent 1e414cf commit 6641319
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 51 deletions.
42 changes: 33 additions & 9 deletions demos/demo-modules/draggable-external-events/component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import {
CalendarEvent,
CalendarEventTimesChangedEvent
CalendarEventTimesChangedEvent,
CalendarView
} from 'angular-calendar';
import { Subject } from 'rxjs';
import { colors } from '../demo-utils/colors';

@Component({
selector: 'mwl-demo-component',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: 'template.html'
templateUrl: 'template.html',
styles: [
`
.drag-active {
position: relative;
z-index: 1;
}
.drag-over {
background-color: #eee;
}
`
]
})
export class DemoComponent {
view: string = 'month';
CalendarView = CalendarView;

viewDate: Date = new Date();
view = CalendarView.Month;

viewDate = new Date();

externalEvents: CalendarEvent[] = [
{
Expand All @@ -33,16 +47,16 @@ export class DemoComponent {

events: CalendarEvent[] = [];

activeDayIsOpen: boolean = false;
activeDayIsOpen = false;

refresh: Subject<any> = new Subject();
refresh = new Subject<void>();

eventDropped({
event,
newStart,
newEnd
}: CalendarEventTimesChangedEvent): void {
const externalIndex: number = this.externalEvents.indexOf(event);
const externalIndex = this.externalEvents.indexOf(event);
if (externalIndex > -1) {
this.externalEvents.splice(externalIndex, 1);
this.events.push(event);
Expand All @@ -51,7 +65,17 @@ export class DemoComponent {
if (newEnd) {
event.end = newEnd;
}
this.viewDate = newStart;
this.activeDayIsOpen = true;
if (this.view === 'month') {
this.viewDate = newStart;
this.activeDayIsOpen = true;
}
this.events = [...this.events];
}

externalDrop(event: CalendarEvent) {
if (this.externalEvents.indexOf(event) === -1) {
this.events = this.events.filter(iEvent => iEvent !== event);
this.externalEvents.push(event);
}
}
}
18 changes: 12 additions & 6 deletions demos/demo-modules/draggable-external-events/template.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
<div class="row">

<div class="col-md-3">
<div class="card">
<div
class="card"
mwlDroppable
(drop)="externalDrop($event.dropData.event)"
dragOverClass="drag-over">
<div class="card-body">
<p *ngIf="externalEvents.length === 0"><em>No events added</em></p>
<ul>
<li
*ngFor="let event of externalEvents"
mwlDraggable
[dropData]="{event: event}"
style="position:relative; z-index: 10">
dragActiveClass="drag-active">
<a
href="javascript:;"
[style.color]="event.color.primary">
Expand All @@ -28,28 +33,29 @@

<div [ngSwitch]="view">
<mwl-calendar-month-view
*ngSwitchCase="'month'"
*ngSwitchCase="CalendarView.Month"
[viewDate]="viewDate"
[events]="events"
[activeDayIsOpen]="activeDayIsOpen"
[refresh]="refresh"
(eventTimesChanged)="eventDropped($event)">
</mwl-calendar-month-view>
<mwl-calendar-week-view
*ngSwitchCase="'week'"
*ngSwitchCase="CalendarView.Week"
[viewDate]="viewDate"
[events]="events"
[refresh]="refresh"
(eventTimesChanged)="eventDropped($event)">
</mwl-calendar-week-view>
<mwl-calendar-day-view
*ngSwitchCase="'day'"
*ngSwitchCase="CalendarView.Day"
[viewDate]="viewDate"
[events]="events"
[refresh]="refresh"
[snapDraggedEvents]="false"
(eventTimesChanged)="eventDropped($event)">
</mwl-calendar-day-view>
</div>
</div>

</div>
</div>
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
"@angular/core": ">=6.0.0 <8.0.0"
},
"dependencies": {
"angular-draggable-droppable": "^4.0.0-beta.3",
"angular-draggable-droppable": "^4.0.0-beta.4",
"angular-resizable-element": "^3.0.0",
"calendar-utils": "^0.2.0-alpha.5",
"positioning": "^1.3.1"
Expand Down
4 changes: 4 additions & 0 deletions src/modules/common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export function isInside(outer: ClientRect, inner: ClientRect): boolean {
);
}

export function roundToNearest(amount: number, precision: number) {
return Math.round(amount / precision) * precision;
}

export const trackByEventId = (index: number, event: CalendarEvent) =>
event.id ? event.id : event;

Expand Down
82 changes: 55 additions & 27 deletions src/modules/day/calendar-day-view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import { CalendarDragHelper } from '../common/calendar-drag-helper.provider';
import { CalendarResizeHelper } from '../common/calendar-resize-helper.provider';
import { CalendarEventTimesChangedEvent } from '../common/calendar-event-times-changed-event.interface';
import { CalendarUtils } from '../common/calendar-utils.provider';
import { validateEvents, trackByEventId } from '../common/util';
import { validateEvents, trackByEventId, roundToNearest } from '../common/util';
import { DateAdapter } from '../../date-adapters/date-adapter';
import { DragEnd } from 'angular-draggable-droppable/draggable.directive';

export interface CalendarDayViewBeforeRenderEvent {
body: {
Expand Down Expand Up @@ -64,15 +65,19 @@ export interface DayViewEventResize {
@Component({
selector: 'mwl-calendar-day-view',
template: `
<div class="cal-day-view" #dayViewContainer>
<div class="cal-day-view">
<mwl-calendar-all-day-event
*ngFor="let event of view.allDayEvents; trackBy:trackByEventId"
[event]="event"
[customTemplate]="allDayEventTemplate"
[eventTitleTemplate]="eventTitleTemplate"
(eventClicked)="eventClicked.emit({event: event})">
</mwl-calendar-all-day-event>
<div class="cal-hour-rows">
<div
class="cal-hour-rows"
#dayEventsContainer
mwlDroppable
(drop)="eventDroppedWithinContainer = true">
<div class="cal-events">
<div
#event
Expand All @@ -86,15 +91,16 @@ export interface DayViewEventResize {
[resizeEdges]="{top: dayEvent.event?.resizable?.beforeStart, bottom: dayEvent.event?.resizable?.afterEnd}"
[resizeSnapGrid]="{top: eventSnapSize, bottom: eventSnapSize}"
[validateResize]="validateResize"
(resizeStart)="resizeStarted(dayEvent, $event, dayViewContainer)"
(resizeStart)="resizeStarted(dayEvent, $event, dayEventsContainer)"
(resizing)="resizing(dayEvent, $event)"
(resizeEnd)="resizeEnded(dayEvent)"
mwlDraggable
[dragAxis]="{x: false, y: dayEvent.event.draggable && currentResizes.size === 0}"
[dragSnapGrid]="{y: eventSnapSize}"
[validateDrag]="validateDrag"
(dragPointerDown)="dragStart(event, dayViewContainer)"
(dragEnd)="eventDragged(dayEvent, $event.y)"
[dropData]="{event: dayEvent.event, isInternal: true}"
[dragAxis]="{x: !snapDraggedEvents && dayEvent.event.draggable && currentResizes.size === 0, y: dayEvent.event.draggable && currentResizes.size === 0}"
[dragSnapGrid]="snapDraggedEvents ? {y: eventSnapSize} : {}"
[validateDrag]="snapDraggedEvents ? validateDrag : false"
(dragPointerDown)="dragStarted(event, dayEventsContainer)"
(dragEnd)="dragEnded(dayEvent, $event)"
[style.marginTop.px]="dayEvent.top"
[style.height.px]="dayEvent.height"
[style.marginLeft.px]="dayEvent.left + 70"
Expand Down Expand Up @@ -226,6 +232,11 @@ export class CalendarDayViewComponent implements OnChanges, OnInit, OnDestroy {
*/
@Input() eventTitleTemplate: TemplateRef<any>;

/**
* Whether to snap events to a grid when dragging
*/
@Input() snapDraggedEvents: boolean = true;

/**
* Called when an event title is clicked
*/
Expand Down Expand Up @@ -280,6 +291,11 @@ export class CalendarDayViewComponent implements OnChanges, OnInit, OnDestroy {
*/
currentResizes: Map<DayViewEvent, DayViewEventResize> = new Map();

/**
* @hidden
*/
eventDroppedWithinContainer = false;

/**
* @hidden
*/
Expand Down Expand Up @@ -379,10 +395,14 @@ export class CalendarDayViewComponent implements OnChanges, OnInit, OnDestroy {
}

eventDropped(
dropEvent: { dropData?: { event?: CalendarEvent } },
dropEvent: { dropData?: { event?: CalendarEvent; isInternal?: boolean } },
segment: DayViewHourSegment
): void {
if (dropEvent.dropData && dropEvent.dropData.event) {
if (
dropEvent.dropData &&
dropEvent.dropData.event &&
!dropEvent.dropData.isInternal
) {
this.eventTimesChanged.emit({
event: dropEvent.dropData.event,
newStart: segment.date
Expand All @@ -393,15 +413,15 @@ export class CalendarDayViewComponent implements OnChanges, OnInit, OnDestroy {
resizeStarted(
event: DayViewEvent,
resizeEvent: ResizeEvent,
dayViewContainer: HTMLElement
dayEventsContainer: HTMLElement
): void {
this.currentResizes.set(event, {
originalTop: event.top,
originalHeight: event.height,
edge: typeof resizeEvent.edges.top !== 'undefined' ? 'top' : 'bottom'
});
const resizeHelper: CalendarResizeHelper = new CalendarResizeHelper(
dayViewContainer
dayEventsContainer
);
this.validateResize = ({ rectangle }) =>
resizeHelper.validateResize({ rectangle });
Expand Down Expand Up @@ -446,29 +466,37 @@ export class CalendarDayViewComponent implements OnChanges, OnInit, OnDestroy {
this.currentResizes.delete(dayEvent);
}

dragStart(event: HTMLElement, dayViewContainer: HTMLElement): void {
dragStarted(event: HTMLElement, dayEventsContainer: HTMLElement): void {
const dragHelper: CalendarDragHelper = new CalendarDragHelper(
dayViewContainer,
dayEventsContainer,
event
);
this.validateDrag = ({ x, y }) =>
this.currentResizes.size === 0 && dragHelper.validateDrag({ x, y });
this.eventDroppedWithinContainer = false;
this.cdr.markForCheck();
}

eventDragged(dayEvent: DayViewEvent, draggedInPixels: number): void {
const pixelAmountInMinutes: number =
MINUTES_IN_HOUR / (this.hourSegments * this.hourSegmentHeight);
const minutesMoved: number = draggedInPixels * pixelAmountInMinutes;
const newStart: Date = this.dateAdapter.addMinutes(
dayEvent.event.start,
minutesMoved
);
let newEnd: Date;
if (dayEvent.event.end) {
newEnd = this.dateAdapter.addMinutes(dayEvent.event.end, minutesMoved);
dragEnded(dayEvent: DayViewEvent, dragEndEvent: DragEnd): void {
if (this.eventDroppedWithinContainer) {
const draggedInPixelsSnapSize = roundToNearest(
dragEndEvent.y,
this.eventSnapSize
);
const pixelAmountInMinutes: number =
MINUTES_IN_HOUR / (this.hourSegments * this.hourSegmentHeight);
const minutesMoved: number =
draggedInPixelsSnapSize * pixelAmountInMinutes;
const newStart: Date = this.dateAdapter.addMinutes(
dayEvent.event.start,
minutesMoved
);
let newEnd: Date;
if (dayEvent.event.end) {
newEnd = this.dateAdapter.addMinutes(dayEvent.event.end, minutesMoved);
}
this.eventTimesChanged.emit({ newStart, newEnd, event: dayEvent.event });
}
this.eventTimesChanged.emit({ newStart, newEnd, event: dayEvent.event });
}

private refreshHourGrid(): void {
Expand Down
Loading

0 comments on commit 6641319

Please sign in to comment.