Skip to content

Commit ef5749c

Browse files
committed
fix(week-view): allow dragging events to span multiple days
BREAKING CHANGE: the drag behaviour now mimicks the resize behaviour where you can drag an event down to move it into the next day. To restore the old behaviour you can use the new `validateEventTimesChanged` input to control where an event can be dragged. Closes #1234
1 parent 7789fda commit ef5749c

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

projects/angular-calendar/src/modules/common/calendar-drag-helper.provider.ts

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isInside, isWithinThreshold } from './util';
1+
import { isInsideLeftAndRight, isWithinThreshold } from './util';
22
import { ValidateDragParams } from 'angular-draggable-droppable';
33

44
export class CalendarDragHelper {
@@ -24,20 +24,34 @@ export class CalendarDragHelper {
2424
dragAlreadyMoved: boolean;
2525
transform: ValidateDragParams['transform'];
2626
}): boolean {
27+
const isDraggedWithinThreshold =
28+
isWithinThreshold({ x, y }) || dragAlreadyMoved;
29+
2730
if (snapDraggedEvents) {
28-
const newRect: ClientRect = Object.assign({}, this.startPosition, {
31+
const inner: ClientRect = Object.assign({}, this.startPosition, {
2932
left: this.startPosition.left + transform.x,
3033
right: this.startPosition.right + transform.x,
3134
top: this.startPosition.top + transform.y,
3235
bottom: this.startPosition.bottom + transform.y,
3336
});
3437

35-
return (
36-
(isWithinThreshold({ x, y }) || dragAlreadyMoved) &&
37-
isInside(this.dragContainerElement.getBoundingClientRect(), newRect)
38-
);
38+
if (isDraggedWithinThreshold) {
39+
const outer = this.dragContainerElement.getBoundingClientRect();
40+
41+
const isTopInside = outer.top < inner.top && inner.top < outer.bottom;
42+
43+
const isBottomInside =
44+
outer.top < inner.bottom && inner.bottom < outer.bottom;
45+
46+
return (
47+
isInsideLeftAndRight(outer, inner) && (isTopInside || isBottomInside)
48+
);
49+
}
50+
51+
/* istanbul ignore next */
52+
return false;
3953
} else {
40-
return isWithinThreshold({ x, y }) || dragAlreadyMoved;
54+
return isDraggedWithinThreshold;
4155
}
4256
}
4357
}

projects/angular-calendar/src/modules/common/util.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,33 @@ export const validateEvents = (events: CalendarEvent[]) => {
1515
return validateEventsWithoutLog(events, warn);
1616
};
1717

18-
export function isInside(outer: ClientRect, inner: ClientRect): boolean {
18+
export function isInsideLeftAndRight(
19+
outer: ClientRect,
20+
inner: ClientRect
21+
): boolean {
1922
return (
2023
Math.floor(outer.left) <= Math.ceil(inner.left) &&
2124
Math.floor(inner.left) <= Math.ceil(outer.right) &&
2225
Math.floor(outer.left) <= Math.ceil(inner.right) &&
23-
Math.floor(inner.right) <= Math.ceil(outer.right) &&
26+
Math.floor(inner.right) <= Math.ceil(outer.right)
27+
);
28+
}
29+
30+
function isInsideTopAndBottom(outer: ClientRect, inner: ClientRect): boolean {
31+
return (
2432
Math.floor(outer.top) <= Math.ceil(inner.top) &&
2533
Math.floor(inner.top) <= Math.ceil(outer.bottom) &&
2634
Math.floor(outer.top) <= Math.ceil(inner.bottom) &&
2735
Math.floor(inner.bottom) <= Math.ceil(outer.bottom)
2836
);
2937
}
3038

39+
export function isInside(outer: ClientRect, inner: ClientRect): boolean {
40+
return (
41+
isInsideLeftAndRight(outer, inner) && isInsideTopAndBottom(outer, inner)
42+
);
43+
}
44+
3145
export function roundToNearest(amount: number, precision: number) {
3246
return Math.round(amount / precision) * precision;
3347
}

projects/angular-calendar/test/calendar-week-view.component.spec.ts

+61
Original file line numberDiff line numberDiff line change
@@ -2652,6 +2652,67 @@ describe('calendarWeekView component', () => {
26522652
fixture.destroy();
26532653
});
26542654

2655+
it('should drag a time event down and into another day', () => {
2656+
const fixture: ComponentFixture<CalendarWeekViewComponent> = TestBed.createComponent(
2657+
CalendarWeekViewComponent
2658+
);
2659+
fixture.componentInstance.viewDate = new Date('2020-05-03');
2660+
fixture.componentInstance.daysInWeek = 2;
2661+
fixture.componentInstance.events = [
2662+
{
2663+
start: moment(new Date('2020-05-03')).startOf('day').hours(20).toDate(),
2664+
end: moment(new Date('2020-05-03')).startOf('day').hours(23).toDate(),
2665+
title: 'foo',
2666+
draggable: true,
2667+
},
2668+
];
2669+
fixture.componentInstance.ngOnChanges({
2670+
viewDate: {},
2671+
events: {},
2672+
});
2673+
fixture.detectChanges();
2674+
document.body.appendChild(fixture.nativeElement);
2675+
const events = fixture.nativeElement.querySelectorAll(
2676+
'.cal-event-container'
2677+
);
2678+
const rect1: ClientRect = events[0].getBoundingClientRect();
2679+
let dragEvent: CalendarEventTimesChangedEvent;
2680+
fixture.componentInstance.eventTimesChanged.pipe(take(1)).subscribe((e) => {
2681+
dragEvent = e;
2682+
});
2683+
triggerDomEvent('mousedown', events[0], {
2684+
clientX: 0,
2685+
clientY: rect1.bottom,
2686+
button: 0,
2687+
});
2688+
fixture.detectChanges();
2689+
triggerDomEvent('mousemove', events[0], {
2690+
clientX: 0,
2691+
clientY: rect1.bottom + 95,
2692+
});
2693+
fixture.detectChanges();
2694+
triggerDomEvent('mouseup', events[0], {
2695+
clientX: 0,
2696+
clientY: rect1.bottom + 95,
2697+
button: 0,
2698+
});
2699+
fixture.detectChanges();
2700+
fixture.destroy();
2701+
expect(dragEvent).to.deep.equal({
2702+
type: 'drag',
2703+
allDay: false,
2704+
event: fixture.componentInstance.events[0],
2705+
newStart: moment(fixture.componentInstance.events[0].start)
2706+
.add(1, 'hour')
2707+
.add(30, 'minutes')
2708+
.toDate(),
2709+
newEnd: moment(fixture.componentInstance.events[0].end)
2710+
.add(1, 'hour')
2711+
.add(30, 'minutes')
2712+
.toDate(),
2713+
});
2714+
});
2715+
26552716
describe('current time marker', () => {
26562717
let clock: any;
26572718
beforeEach(() => {

0 commit comments

Comments
 (0)