Skip to content

Commit 6a72448

Browse files
martinswaremattlewis92
authored andcommitted
feat(week-view): add hourDuration option
Closes #1080
1 parent f1e2831 commit 6a72448

File tree

10 files changed

+306
-11
lines changed

10 files changed

+306
-11
lines changed

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

+11-6
Original file line numberDiff line numberDiff line change
@@ -60,34 +60,39 @@ const MINUTES_IN_HOUR = 60;
6060

6161
function getPixelAmountInMinutes(
6262
hourSegments: number,
63-
hourSegmentHeight: number
63+
hourSegmentHeight: number,
64+
hourDuration?: number
6465
) {
65-
return MINUTES_IN_HOUR / (hourSegments * hourSegmentHeight);
66+
return (hourDuration || MINUTES_IN_HOUR) / (hourSegments * hourSegmentHeight);
6667
}
6768

6869
export function getMinutesMoved(
6970
movedY: number,
7071
hourSegments: number,
7172
hourSegmentHeight: number,
72-
eventSnapSize: number
73+
eventSnapSize: number,
74+
hourDuration?: number
7375
): number {
7476
const draggedInPixelsSnapSize = roundToNearest(
7577
movedY,
7678
eventSnapSize || hourSegmentHeight
7779
);
7880
const pixelAmountInMinutes = getPixelAmountInMinutes(
7981
hourSegments,
80-
hourSegmentHeight
82+
hourSegmentHeight,
83+
hourDuration
8184
);
8285
return draggedInPixelsSnapSize * pixelAmountInMinutes;
8386
}
8487

8588
export function getMinimumEventHeightInMinutes(
8689
hourSegments: number,
87-
hourSegmentHeight: number
90+
hourSegmentHeight: number,
91+
hourDuration?: number
8892
) {
8993
return (
90-
getPixelAmountInMinutes(hourSegments, hourSegmentHeight) * hourSegmentHeight
94+
getPixelAmountInMinutes(hourSegments, hourSegmentHeight, hourDuration) *
95+
hourSegmentHeight
9196
);
9297
}
9398

projects/angular-calendar/src/modules/day/calendar-day-view.component.ts

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type CalendarDayViewBeforeRenderEvent = CalendarWeekViewBeforeRenderEvent
3232
[viewDate]="viewDate"
3333
[events]="events"
3434
[hourSegments]="hourSegments"
35+
[hourDuration]="hourDuration"
3536
[hourSegmentHeight]="hourSegmentHeight"
3637
[dayStartHour]="dayStartHour"
3738
[dayStartMinute]="dayStartMinute"
@@ -80,6 +81,11 @@ export class CalendarDayViewComponent {
8081
*/
8182
@Input() hourSegmentHeight: number = 30;
8283

84+
/**
85+
* The duration of each segment group in minutes
86+
*/
87+
@Input() hourDuration: number;
88+
8389
/**
8490
* The day start hours in 24 hour time. Must be 0-23
8591
*/

projects/angular-calendar/src/modules/week/calendar-week-view-current-time-marker.component.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export class CalendarWeekViewCurrentTimeMarkerComponent implements OnChanges {
5757

5858
@Input() hourSegments: number;
5959

60+
@Input() hourDuration: number;
61+
6062
@Input() hourSegmentHeight: number;
6163

6264
@Input() customTemplate: TemplateRef<any>;
@@ -80,7 +82,8 @@ export class CalendarWeekViewCurrentTimeMarkerComponent implements OnChanges {
8082
this.dayEndMinute
8183
);
8284
const hourHeightModifier =
83-
(this.hourSegments * this.hourSegmentHeight) / 60;
85+
(this.hourSegments * this.hourSegmentHeight) /
86+
(this.hourDuration || 60);
8487
const now = new Date();
8588
return {
8689
isVisible:

projects/angular-calendar/src/modules/week/calendar-week-view.component.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export interface CalendarWeekViewBeforeRenderEvent extends WeekView {
249249
[dayEndHour]="dayEndHour"
250250
[dayEndMinute]="dayEndMinute"
251251
[hourSegments]="hourSegments"
252+
[hourDuration]="hourDuration"
252253
[hourSegmentHeight]="hourSegmentHeight"
253254
[customTemplate]="currentTimeMarkerTemplate"
254255
></mwl-calendar-week-view-current-time-marker>
@@ -497,6 +498,11 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
497498
*/
498499
@Input() hourSegments: number = 2;
499500

501+
/**
502+
* The duration of each segment group in minutes
503+
*/
504+
@Input() hourDuration: number;
505+
500506
/**
501507
* The height in pixels of each hour segment
502508
*/
@@ -742,6 +748,7 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
742748
changes.dayEndHour ||
743749
changes.dayEndMinute ||
744750
changes.hourSegments ||
751+
changes.hourDuration ||
745752
changes.weekStartsOn ||
746753
changes.weekendDays ||
747754
changes.excludeDays ||
@@ -1130,6 +1137,7 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
11301137
precision: this.precision,
11311138
absolutePositionedEvents: true,
11321139
hourSegments: this.hourSegments,
1140+
hourDuration: this.hourDuration,
11331141
dayStart: {
11341142
hour: this.dayStartHour,
11351143
minute: this.dayStartMinute,
@@ -1162,7 +1170,8 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
11621170
dragEndEvent.y,
11631171
this.hourSegments,
11641172
this.hourSegmentHeight,
1165-
this.eventSnapSize
1173+
this.eventSnapSize,
1174+
this.hourDuration
11661175
)
11671176
: 0;
11681177

@@ -1241,7 +1250,8 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
12411250
) {
12421251
const minimumEventHeight = getMinimumEventHeightInMinutes(
12431252
this.hourSegments,
1244-
this.hourSegmentHeight
1253+
this.hourSegmentHeight,
1254+
this.hourDuration
12451255
);
12461256
const newEventDates = {
12471257
start: calendarEvent.start,
@@ -1301,7 +1311,8 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
13011311
resizeEvent.edges.top as number,
13021312
this.hourSegments,
13031313
this.hourSegmentHeight,
1304-
this.eventSnapSize
1314+
this.eventSnapSize,
1315+
this.hourDuration
13051316
);
13061317
const newStart = this.dateAdapter.addMinutes(
13071318
newEventDates.start,
@@ -1317,7 +1328,8 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
13171328
resizeEvent.edges.bottom as number,
13181329
this.hourSegments,
13191330
this.hourSegmentHeight,
1320-
this.eventSnapSize
1331+
this.eventSnapSize,
1332+
this.hourDuration
13211333
);
13221334
const newEnd = this.dateAdapter.addMinutes(
13231335
newEventDates.end,

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

+141
Original file line numberDiff line numberDiff line change
@@ -2228,6 +2228,79 @@ describe('calendarWeekView component', () => {
22282228
});
22292229
});
22302230

2231+
it('should drag time events to different days and columns while snapping to a grid and excluding weekends with custom hour duration', () => {
2232+
const fixture: ComponentFixture<CalendarWeekViewComponent> = TestBed.createComponent(
2233+
CalendarWeekViewComponent
2234+
);
2235+
fixture.componentInstance.viewDate = new Date('2019-03-01');
2236+
fixture.componentInstance.excludeDays = [0, 6];
2237+
fixture.componentInstance.daysInWeek = 4;
2238+
fixture.componentInstance.events = [
2239+
{
2240+
start: moment(new Date('2019-03-01'))
2241+
.startOf('day')
2242+
.add(4, 'hours')
2243+
.toDate(),
2244+
end: moment(new Date('2019-03-01'))
2245+
.startOf('day')
2246+
.add(18, 'hours')
2247+
.toDate(),
2248+
title: 'foo',
2249+
draggable: true
2250+
}
2251+
];
2252+
fixture.componentInstance.hourDuration = 40;
2253+
fixture.componentInstance.ngOnChanges({
2254+
viewDate: {},
2255+
events: {},
2256+
excludeDays: {},
2257+
daysInWeek: {},
2258+
hourColumns: {}
2259+
});
2260+
fixture.detectChanges();
2261+
document.body.appendChild(fixture.nativeElement);
2262+
const events = fixture.nativeElement.querySelectorAll(
2263+
'.cal-event-container'
2264+
);
2265+
const dayWidth: number = events[0].parentElement.offsetWidth;
2266+
const rect1: ClientRect = events[0].getBoundingClientRect();
2267+
let dragEvent: CalendarEventTimesChangedEvent;
2268+
fixture.componentInstance.eventTimesChanged.pipe(take(1)).subscribe(e => {
2269+
dragEvent = e;
2270+
});
2271+
triggerDomEvent('mousedown', events[0], {
2272+
clientX: rect1.right,
2273+
clientY: rect1.bottom,
2274+
button: 0
2275+
});
2276+
fixture.detectChanges();
2277+
triggerDomEvent('mousemove', events[0], {
2278+
clientX: rect1.right + dayWidth - 5,
2279+
clientY: rect1.bottom + 95
2280+
});
2281+
fixture.detectChanges();
2282+
triggerDomEvent('mouseup', events[0], {
2283+
clientX: rect1.right + dayWidth - 5,
2284+
clientY: rect1.bottom + 95,
2285+
button: 0
2286+
});
2287+
fixture.detectChanges();
2288+
fixture.destroy();
2289+
expect(dragEvent).to.deep.equal({
2290+
type: 'drag',
2291+
allDay: false,
2292+
event: fixture.componentInstance.events[0],
2293+
newStart: moment(fixture.componentInstance.events[0].start)
2294+
.add(3, 'days')
2295+
.add(1, 'hour')
2296+
.toDate(),
2297+
newEnd: moment(fixture.componentInstance.events[0].end)
2298+
.add(3, 'days')
2299+
.add(1, 'hour')
2300+
.toDate()
2301+
});
2302+
});
2303+
22312304
it('should preserve css classes on hour segments when dragging an event', () => {
22322305
const fixture: ComponentFixture<CalendarWeekViewComponent> = TestBed.createComponent(
22332306
CalendarWeekViewComponent
@@ -2351,6 +2424,74 @@ describe('calendarWeekView component', () => {
23512424
});
23522425
});
23532426

2427+
it('should resize a time event to the minimum height with custom hour duration', () => {
2428+
const fixture: ComponentFixture<CalendarWeekViewComponent> = TestBed.createComponent(
2429+
CalendarWeekViewComponent
2430+
);
2431+
fixture.componentInstance.viewDate = new Date('2018-07-29');
2432+
fixture.componentInstance.events = [
2433+
{
2434+
start: moment(new Date('2018-07-29'))
2435+
.startOf('day')
2436+
.add(4, 'hours')
2437+
.toDate(),
2438+
end: moment(new Date('2018-07-29'))
2439+
.startOf('day')
2440+
.add(6, 'hours')
2441+
.toDate(),
2442+
title: 'foo',
2443+
resizable: {
2444+
afterEnd: true
2445+
}
2446+
}
2447+
];
2448+
fixture.componentInstance.hourDuration = 40;
2449+
fixture.componentInstance.hourSegmentHeight = 20;
2450+
fixture.componentInstance.ngOnChanges({
2451+
viewDate: {},
2452+
events: {},
2453+
hourDuration: {},
2454+
hourSegmentHeight: {}
2455+
});
2456+
fixture.detectChanges();
2457+
document.body.appendChild(fixture.nativeElement);
2458+
const event: HTMLElement = fixture.nativeElement.querySelectorAll(
2459+
'.cal-event-container'
2460+
)[0];
2461+
const rect: ClientRect = event.getBoundingClientRect();
2462+
const resizeHandle = event.querySelector('.cal-resize-handle-after-end');
2463+
let resizeEvent: CalendarEventTimesChangedEvent;
2464+
fixture.componentInstance.eventTimesChanged.pipe(take(1)).subscribe(e => {
2465+
resizeEvent = e;
2466+
});
2467+
triggerDomEvent('mousedown', resizeHandle, {
2468+
clientX: rect.right,
2469+
clientY: rect.bottom,
2470+
button: 0
2471+
});
2472+
fixture.detectChanges();
2473+
triggerDomEvent('mousemove', document.body, {
2474+
clientX: rect.right,
2475+
clientY: rect.bottom - 120
2476+
});
2477+
fixture.detectChanges();
2478+
expect(event.getBoundingClientRect().height).to.equal(20);
2479+
triggerDomEvent('mouseup', document.body, {
2480+
clientX: rect.right,
2481+
clientY: rect.bottom - 120,
2482+
button: 0
2483+
});
2484+
fixture.detectChanges();
2485+
expect(resizeEvent).to.deep.equal({
2486+
type: 'resize',
2487+
event: fixture.componentInstance.events[0],
2488+
newStart: fixture.componentInstance.events[0].start,
2489+
newEnd: moment(fixture.componentInstance.events[0].start)
2490+
.add(20, 'minutes')
2491+
.toDate()
2492+
});
2493+
});
2494+
23542495
it('should allow css variables for colors', () => {
23552496
const style = document.createElement('style');
23562497
style.setAttribute('type', 'text/css');

projects/demos/app/demo-app.module.ts

+10
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ import { ClipboardModule } from 'ngx-clipboard';
140140
label: 'Clickable times',
141141
},
142142
},
143+
{
144+
path: 'custom-hour-duration',
145+
loadChildren: () =>
146+
import('./demo-modules/custom-hour-duration/module').then(
147+
(m) => m.DemoModule
148+
),
149+
data: {
150+
label: 'Custom hour duration',
151+
},
152+
},
143153
{
144154
path: 'day-view-start-end',
145155
loadChildren: () =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Component, ChangeDetectionStrategy } from '@angular/core';
2+
import { setMinutes, setHours } from 'date-fns';
3+
import { Subject } from 'rxjs';
4+
import {
5+
CalendarEvent,
6+
CalendarEventTimesChangedEvent,
7+
CalendarView,
8+
} from 'angular-calendar';
9+
10+
@Component({
11+
selector: 'mwl-demo-component',
12+
changeDetection: ChangeDetectionStrategy.OnPush,
13+
templateUrl: 'template.html',
14+
})
15+
export class DemoComponent {
16+
view: CalendarView = CalendarView.Week;
17+
18+
viewDate = new Date();
19+
20+
events: CalendarEvent[] = [
21+
{
22+
start: setHours(setMinutes(new Date(), 20), 15),
23+
end: setHours(setMinutes(new Date(), 40), 17),
24+
title: 'An event',
25+
resizable: {
26+
afterEnd: true,
27+
},
28+
draggable: true,
29+
},
30+
];
31+
32+
refresh = new Subject();
33+
34+
eventTimesChanged({
35+
event,
36+
newStart,
37+
newEnd,
38+
}: CalendarEventTimesChangedEvent): void {
39+
event.start = newStart;
40+
event.end = newEnd;
41+
this.refresh.next();
42+
}
43+
}

0 commit comments

Comments
 (0)