Skip to content

Commit 7a2714b

Browse files
author
Matt Lewis
committed
feat(monthView): Add drag and drop functionality
BREAKING CHANGE: A dependency on the `angular-draggable-droppable` library has been added. System.js users will need to add this to their config: ``` 'angular-draggable-droppable': 'npm:angular-draggable-droppable/dist/umd/angular-draggable-droppable.js' ``` First part of #10
1 parent 881bfa2 commit 7a2714b

12 files changed

+173
-16
lines changed

demo/demo.component.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ const colors: any = {
7979
[events]="events"
8080
[refresh]="refresh"
8181
[activeDayIsOpen]="activeDayIsOpen"
82-
(dayClicked)="dayClicked($event.day)">
82+
(dayClicked)="dayClicked($event.day)"
83+
(eventTimesChanged)="eventTimesChanged($event)">
8384
</mwl-calendar-month-view>
8485
<mwl-calendar-week-view
8586
*ngSwitchCase="'week'"
@@ -138,13 +139,14 @@ export class DemoComponent {
138139
}, {
139140
start: addHours(startOfDay(new Date()), 2),
140141
end: new Date(),
141-
title: 'A resizable event',
142+
title: 'A draggable and resizable event',
142143
color: colors.yellow,
143144
actions: this.actions,
144145
resizable: {
145146
beforeStart: true,
146147
afterEnd: true
147-
}
148+
},
149+
draggable: true
148150
}];
149151

150152
activeDayIsOpen: boolean = true;

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@
123123
},
124124
"dependencies": {
125125
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.0",
126+
"angular-draggable-droppable": "~0.1.1",
126127
"angular-resizable-element": "~0.5.0",
127-
"calendar-utils": "0.0.38",
128+
"calendar-utils": "0.0.39",
128129
"date-fns": "^1.15.1"
129130
}
130131
}

scss/month-view.scss

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
font-size: 1.9em;
120120
}
121121

122+
.cal-day-cell.cal-drag-over {
123+
background-color: darken(#ededed, 5%) !important;
124+
}
125+
122126
.cal-open-day-events {
123127
padding: 15px;
124128
color: white;

src/calendar.module.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NgModule, ModuleWithProviders } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { ResizableModule } from 'angular-resizable-element';
4+
import { DragAndDropModule } from 'angular-draggable-droppable';
45
import { CalendarDayViewComponent } from './components/day/calendarDayView.component';
56
import { CalendarWeekViewComponent } from './components/week/calendarWeekView.component';
67
import { CalendarMonthViewComponent } from './components/month/calendarMonthView.component';
@@ -38,8 +39,17 @@ import { CalendarDateFormatter } from './providers/calendarDateFormatter.provide
3839
CalendarDate,
3940
CalendarEventTitlePipe
4041
],
41-
imports: [CommonModule, ResizableModule],
42-
exports: [CalendarDayViewComponent, CalendarWeekViewComponent, CalendarMonthViewComponent, CalendarDate],
42+
imports: [
43+
CommonModule,
44+
ResizableModule,
45+
DragAndDropModule
46+
],
47+
exports: [
48+
CalendarDayViewComponent,
49+
CalendarWeekViewComponent,
50+
CalendarMonthViewComponent,
51+
CalendarDate
52+
],
4353
entryComponents: [CalendarTooltipWindowComponent]
4454
})
4555
export class CalendarModule {

src/components/day/calendarDayView.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { getDayView, getDayViewHourGrid, CalendarEvent, DayView, DayViewHour } from 'calendar-utils';
1515
import { Subject } from 'rxjs/Subject';
1616
import { Subscription } from 'rxjs/Subscription';
17-
import { CalendarEventTimesChangedEvent } from './../../interfaces/calendarEventTimesChangedEvent.interface';
17+
import { CalendarEventTimesChangedEvent } from '../../interfaces/calendarEventTimesChangedEvent.interface';
1818

1919
const SEGMENT_HEIGHT: number = 30;
2020

src/components/month/calendarMonthCell.component.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ import { MonthViewDay } from 'calendar-utils';
1010
<span class="cal-day-number">{{ day.date | calendarDate:'monthViewDayNumber':locale }}</span>
1111
</div>
1212
<div class="cal-events">
13-
<span
13+
<div
1414
class="cal-event"
1515
*ngFor="let event of day.events"
1616
[style.backgroundColor]="event.color.primary"
1717
[ngClass]="event?.cssClass"
1818
(mouseenter)="highlightDay.emit({event: event})"
1919
(mouseleave)="unhighlightDay.emit({event: event})"
2020
[mwlCalendarTooltip]="event | calendarEventTitle:'monthTooltip'"
21-
[tooltipPlacement]="tooltipPlacement">
22-
</span>
21+
[tooltipPlacement]="tooltipPlacement"
22+
mwlDraggable
23+
[dropData]="{event: event}"
24+
[dragAxis]="{x: event.draggable, y: event.draggable}">
25+
</div>
2326
</div>
2427
`,
2528
host: {

src/components/month/calendarMonthView.component.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ import {
2222
import { Subject } from 'rxjs/Subject';
2323
import { Subscription } from 'rxjs/Subscription';
2424
import isSameDay from 'date-fns/is_same_day';
25+
import setDate from 'date-fns/set_date';
26+
import setMonth from 'date-fns/set_month';
27+
import setYear from 'date-fns/set_year';
28+
import getDate from 'date-fns/get_date';
29+
import getMonth from 'date-fns/get_month';
30+
import getYear from 'date-fns/get_year';
31+
import differenceInSeconds from 'date-fns/difference_in_seconds';
32+
import addSeconds from 'date-fns/add_seconds';
33+
import { CalendarEventTimesChangedEvent } from '../../interfaces/calendarEventTimesChangedEvent.interface';
2534

2635
@Component({
2736
selector: 'mwl-calendar-month-view',
@@ -38,13 +47,18 @@ import isSameDay from 'date-fns/is_same_day';
3847
<div class="cal-cell-row">
3948
<mwl-calendar-month-cell
4049
*ngFor="let day of view.days | slice : rowIndex : rowIndex + 7"
50+
[class.cal-drag-over]="day.dragOver"
4151
[day]="day"
4252
[openDay]="openDay"
4353
[locale]="locale"
4454
[tooltipPlacement]="tooltipPlacement"
4555
(click)="dayClicked.emit({day: day})"
4656
(highlightDay)="toggleDayHighlight($event.event, true)"
47-
(unhighlightDay)="toggleDayHighlight($event.event, false)">
57+
(unhighlightDay)="toggleDayHighlight($event.event, false)"
58+
mwlDroppable
59+
(dragEnter)="day.dragOver = true"
60+
(dragLeave)="day.dragOver = false"
61+
(drop)="day.dragOver = false; eventDropped(day, $event.dropData.event)">
4862
</mwl-calendar-month-cell>
4963
</div>
5064
<mwl-calendar-open-day-events
@@ -110,6 +124,11 @@ export class CalendarMonthViewComponent implements OnChanges, OnInit, OnDestroy
110124
*/
111125
@Output() eventClicked: EventEmitter<{event: CalendarEvent}> = new EventEmitter<{event: CalendarEvent}>();
112126

127+
/**
128+
* Called when an event is dragged and dropped
129+
*/
130+
@Output() eventTimesChanged: EventEmitter<CalendarEventTimesChangedEvent> = new EventEmitter<CalendarEventTimesChangedEvent>();
131+
113132
/**
114133
* @private
115134
*/
@@ -195,6 +214,22 @@ export class CalendarMonthViewComponent implements OnChanges, OnInit, OnDestroy
195214
});
196215
}
197216

217+
/**
218+
* @private
219+
*/
220+
eventDropped(day: MonthViewDay, event: CalendarEvent): void {
221+
const year: number = getYear(day.date);
222+
const month: number = getMonth(day.date);
223+
const date: number = getDate(day.date);
224+
const newStart: Date = setYear(setMonth(setDate(event.start, date), month), year);
225+
let newEnd: Date;
226+
if (event.end) {
227+
const secondsDiff: number = differenceInSeconds(newStart, event.start);
228+
newEnd = addSeconds(event.end, secondsDiff);
229+
}
230+
this.eventTimesChanged.emit({event, newStart, newEnd});
231+
}
232+
198233
private refreshHeader(): void {
199234
this.columnHeaders = getWeekViewHeader({
200235
viewDate: this.viewDate,

src/components/month/calendarOpenDayEvents.component.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import { CalendarEvent } from 'calendar-utils';
1919
<div
2020
*ngFor="let event of events"
2121
[ngClass]="event?.cssClass">
22-
<span class="cal-event" [style.backgroundColor]="event.color.primary"></span>
22+
<span
23+
class="cal-event"
24+
[style.backgroundColor]="event.color.primary">
25+
</span>
2326
<mwl-calendar-event-title
2427
[event]="event"
2528
view="month"

src/interfaces/calendarEventTimesChangedEvent.interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import { CalendarEvent } from 'calendar-utils';
33
export interface CalendarEventTimesChangedEvent {
44
event: CalendarEvent;
55
newStart: Date;
6-
newEnd: Date;
6+
newEnd?: Date;
77
}

test/calendarMonthView.component.spec.ts

+50
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import { CalendarMonthViewComponent } from './../src/components/month/calendarMonthView.component';
1818
import { Subject } from 'rxjs/Subject';
1919
import { triggerDomEvent } from './util';
20+
import { CalendarEventTimesChangedEvent } from '../src/interfaces/calendarEventTimesChangedEvent.interface';
2021

2122
describe('calendarMonthView component', () => {
2223

@@ -308,4 +309,53 @@ describe('calendarMonthView component', () => {
308309
fixture.destroy();
309310
});
310311

312+
it('should allow events to be dragged and dropped', () => {
313+
314+
const fixture: ComponentFixture<CalendarMonthViewComponent> = TestBed.createComponent(CalendarMonthViewComponent);
315+
fixture.componentInstance.viewDate = new Date('2016-12-05');
316+
fixture.componentInstance.events = [{
317+
start: new Date(2016, 11, 5, 10, 39, 14),
318+
end: new Date(2016, 11, 5, 15, 11, 5),
319+
title: 'draggable event',
320+
color: {
321+
primary: 'blue',
322+
secondary: 'rgb(238, 238, 238)'
323+
},
324+
draggable: true
325+
}];
326+
fixture.componentInstance.ngOnChanges({viewDate: {}});
327+
let dragEvent: CalendarEventTimesChangedEvent;
328+
fixture.componentInstance.eventTimesChanged.subscribe(event => {
329+
dragEvent = event;
330+
});
331+
fixture.detectChanges();
332+
document.body.appendChild(fixture.nativeElement);
333+
const cells: HTMLElement[] = fixture.nativeElement.querySelectorAll('.cal-day-cell');
334+
const event: HTMLElement = fixture.nativeElement.querySelector('.cal-event');
335+
const dragToCellPosition: ClientRect = cells[10].getBoundingClientRect();
336+
const eventStartPosition: ClientRect = event.getBoundingClientRect();
337+
event.style.width = '10px';
338+
event.style.height = '10px';
339+
triggerDomEvent('mousedown', event, {clientX: eventStartPosition.left, clientY: eventStartPosition.top});
340+
fixture.detectChanges();
341+
triggerDomEvent('mousemove', document.body, {clientX: dragToCellPosition.left, clientY: dragToCellPosition.top});
342+
fixture.detectChanges();
343+
expect(cells[10].classList.contains('cal-drag-over')).to.be.true;
344+
const eventAfterDragPosition: ClientRect = event.getBoundingClientRect();
345+
const movedLeft: number = dragToCellPosition.left - eventStartPosition.left;
346+
expect(Math.round(eventAfterDragPosition.left)).to.equal(eventStartPosition.left + movedLeft);
347+
const movedTop: number = dragToCellPosition.top - eventStartPosition.top;
348+
expect(Math.round(eventAfterDragPosition.top)).to.equal(eventStartPosition.top + movedTop);
349+
triggerDomEvent('mouseup', document.body, {clientX: dragToCellPosition.left, clientY: dragToCellPosition.top});
350+
fixture.detectChanges();
351+
expect(cells[10].classList.contains('cal-drag-over')).to.be.false;
352+
fixture.destroy();
353+
expect(dragEvent).to.deep.equal({
354+
event: fixture.componentInstance.events[0],
355+
newStart: new Date(2016, 11, 7, 10, 39, 14),
356+
newEnd: new Date(2016, 11, 7, 15, 11, 5)
357+
});
358+
359+
});
360+
311361
});

webpack.config.umd.ts

+45
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,55 @@ module.exports = {
6969
commonjs: 'date-fns/add_days/index',
7070
commonjs2: 'date-fns/add_days/index'
7171
},
72+
'date-fns/set_date': {
73+
root: ['dateFns', 'setDate'],
74+
commonjs: 'date-fns/set_date/index',
75+
commonjs2: 'date-fns/set_date/index'
76+
},
77+
'date-fns/set_month': {
78+
root: ['dateFns', 'setMonth'],
79+
commonjs: 'date-fns/set_month/index',
80+
commonjs2: 'date-fns/set_month/index'
81+
},
82+
'date-fns/set_year': {
83+
root: ['dateFns', 'setYear'],
84+
commonjs: 'date-fns/set_year/index',
85+
commonjs2: 'date-fns/set_year/index'
86+
},
87+
'date-fns/get_date': {
88+
root: ['dateFns', 'getDate'],
89+
commonjs: 'date-fns/get_date/index',
90+
commonjs2: 'date-fns/get_date/index'
91+
},
92+
'date-fns/get_month': {
93+
root: ['dateFns', 'getMonth'],
94+
commonjs: 'date-fns/get_month/index',
95+
commonjs2: 'date-fns/get_month/index'
96+
},
97+
'date-fns/get_year': {
98+
root: ['dateFns', 'getYear'],
99+
commonjs: 'date-fns/get_year/index',
100+
commonjs2: 'date-fns/get_year/index'
101+
},
102+
'date-fns/difference_in_seconds': {
103+
root: ['dateFns', 'differenceInSeconds'],
104+
commonjs: 'date-fns/difference_in_seconds/index',
105+
commonjs2: 'date-fns/difference_in_seconds/index'
106+
},
107+
'date-fns/add_seconds': {
108+
root: ['dateFns', 'addSeconds'],
109+
commonjs: 'date-fns/add_seconds/index',
110+
commonjs2: 'date-fns/add_seconds/index'
111+
},
72112
'angular-resizable-element': {
73113
root: 'angularResizableElement',
74114
commonjs: 'angular-resizable-element',
75115
commonjs2: 'angular-resizable-element'
116+
},
117+
'angular-draggable-droppable': {
118+
root: 'angularDraggableDroppable',
119+
commonjs: 'angular-draggable-droppable',
120+
commonjs2: 'angular-draggable-droppable'
76121
}
77122
},
78123
module: {

yarn.lock

+7-3
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ amdefine@>=0.0.4:
186186
version "1.0.1"
187187
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
188188

189+
angular-draggable-droppable@~0.1.1:
190+
version "0.1.1"
191+
resolved "https://registry.yarnpkg.com/angular-draggable-droppable/-/angular-draggable-droppable-0.1.1.tgz#98efac5b98581727210cf6c66464a9c0b04b91af"
192+
189193
angular-resizable-element@~0.5.0:
190194
version "0.5.3"
191195
resolved "https://registry.yarnpkg.com/angular-resizable-element/-/angular-resizable-element-0.5.3.tgz#14170ede1c1f4d170d41ed8d626692e4609f445e"
@@ -640,9 +644,9 @@ [email protected]:
640644
version "2.4.0"
641645
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339"
642646

643-
644-
version "0.0.38"
645-
resolved "https://registry.yarnpkg.com/calendar-utils/-/calendar-utils-0.0.38.tgz#13569692ecdf9c20fe9db67122323cd8f9f86779"
647+
648+
version "0.0.39"
649+
resolved "https://registry.yarnpkg.com/calendar-utils/-/calendar-utils-0.0.39.tgz#8b12c85873a61d730212da1bc327166a46c50951"
646650

647651
648652
version "1.0.0"

0 commit comments

Comments
 (0)