Skip to content

Commit d3872b1

Browse files
committed
feat(week-view): add current time marker
Closes #1102
1 parent 89d1415 commit d3872b1

11 files changed

+258
-22
lines changed

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ export const trackByWeekTimeEvent = (
5858

5959
const MINUTES_IN_HOUR = 60;
6060

61+
function getPixelAmountInMinutes(
62+
hourSegments: number,
63+
hourSegmentHeight: number
64+
) {
65+
return MINUTES_IN_HOUR / (hourSegments * hourSegmentHeight);
66+
}
67+
6168
export function getMinutesMoved(
6269
movedY: number,
6370
hourSegments: number,
@@ -68,8 +75,10 @@ export function getMinutesMoved(
6875
movedY,
6976
eventSnapSize || hourSegmentHeight
7077
);
71-
const pixelAmountInMinutes =
72-
MINUTES_IN_HOUR / (hourSegments * hourSegmentHeight);
78+
const pixelAmountInMinutes = getPixelAmountInMinutes(
79+
hourSegments,
80+
hourSegmentHeight
81+
);
7382
return draggedInPixelsSnapSize * pixelAmountInMinutes;
7483
}
7584

@@ -78,7 +87,7 @@ export function getMinimumEventHeightInMinutes(
7887
hourSegmentHeight: number
7988
) {
8089
return (
81-
(MINUTES_IN_HOUR / (hourSegments * hourSegmentHeight)) * hourSegmentHeight
90+
getPixelAmountInMinutes(hourSegments, hourSegmentHeight) * hourSegmentHeight
8291
);
8392
}
8493

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

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export type CalendarDayViewBeforeRenderEvent = CalendarWeekViewBeforeRenderEvent
5050
[eventActionsTemplate]="eventActionsTemplate"
5151
[snapDraggedEvents]="snapDraggedEvents"
5252
[allDayEventsLabelTemplate]="allDayEventsLabelTemplate"
53+
[currentTimeMarkerTemplate]="currentTimeMarkerTemplate"
5354
(eventClicked)="eventClicked.emit($event)"
5455
(hourSegmentClicked)="hourSegmentClicked.emit($event)"
5556
(eventTimesChanged)="eventTimesChanged.emit($event)"
@@ -165,6 +166,11 @@ export class CalendarDayViewComponent {
165166
*/
166167
@Input() allDayEventsLabelTemplate: TemplateRef<any>;
167168

169+
/**
170+
* A custom template to use for the current time marker
171+
*/
172+
@Input() currentTimeMarkerTemplate: TemplateRef<any>;
173+
168174
/**
169175
* Called when an event title is clicked
170176
*/

projects/angular-calendar/src/modules/day/calendar-day-view.scss

+5
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,9 @@
1111
.cal-day-column {
1212
border-left: 0;
1313
}
14+
15+
.cal-current-time-marker {
16+
margin-left: 70px;
17+
width: calc(100% - 70px);
18+
}
1419
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import {
2+
Component,
3+
Input,
4+
OnChanges,
5+
SimpleChanges,
6+
TemplateRef
7+
} from '@angular/core';
8+
import { BehaviorSubject, interval, Observable } from 'rxjs';
9+
import { switchMapTo, startWith, map } from 'rxjs/operators';
10+
import { DateAdapter } from '../../date-adapters/date-adapter';
11+
12+
@Component({
13+
selector: 'mwl-calendar-week-view-current-time-marker',
14+
template: `
15+
<ng-template
16+
#defaultTemplate
17+
let-columnDate="columnDate"
18+
let-dayStartHour="dayStartHour"
19+
let-dayStartMinute="dayStartMinute"
20+
let-dayEndHour="dayEndHour"
21+
let-dayEndMinute="dayEndMinute"
22+
>
23+
<div
24+
class="cal-current-time-marker"
25+
*ngIf="(marker$ | async)?.isVisible"
26+
[style.top.px]="(marker$ | async)?.top"
27+
></div>
28+
</ng-template>
29+
<ng-template
30+
[ngTemplateOutlet]="customTemplate || defaultTemplate"
31+
[ngTemplateOutletContext]="{
32+
columnDate: columnDate,
33+
dayStartHour: dayStartHour,
34+
dayStartMinute: dayStartMinute,
35+
dayEndHour: dayEndHour,
36+
dayEndMinute: dayEndMinute
37+
}"
38+
>
39+
</ng-template>
40+
`
41+
})
42+
export class CalendarWeekViewCurrentTimeMarkerComponent implements OnChanges {
43+
@Input() columnDate: Date;
44+
45+
@Input() dayStartHour: number;
46+
47+
@Input() dayStartMinute: number;
48+
49+
@Input() dayEndHour: number;
50+
51+
@Input() dayEndMinute: number;
52+
53+
@Input() hourSegments: number;
54+
55+
@Input() hourSegmentHeight: number;
56+
57+
@Input() customTemplate: TemplateRef<any>;
58+
59+
private columnDate$ = new BehaviorSubject<Date>(this.columnDate);
60+
61+
marker$: Observable<{ isVisible: boolean; top: number }> = interval(
62+
60 * 1000
63+
).pipe(
64+
startWith(0),
65+
switchMapTo(this.columnDate$),
66+
map(columnDate => {
67+
const startOfDay = this.dateAdapter.setMinutes(
68+
this.dateAdapter.setHours(columnDate, this.dayStartHour),
69+
this.dayStartMinute
70+
);
71+
const endOfDay = this.dateAdapter.setMinutes(
72+
this.dateAdapter.setHours(columnDate, this.dayEndHour),
73+
this.dayEndMinute
74+
);
75+
const hourHeightModifier =
76+
(this.hourSegments * this.hourSegmentHeight) / 60;
77+
const now = new Date();
78+
return {
79+
isVisible:
80+
this.dateAdapter.isSameDay(columnDate, now) &&
81+
now >= startOfDay &&
82+
now <= endOfDay,
83+
top:
84+
this.dateAdapter.differenceInMinutes(now, startOfDay) *
85+
hourHeightModifier
86+
};
87+
})
88+
);
89+
90+
constructor(private dateAdapter: DateAdapter) {}
91+
92+
ngOnChanges(changes: SimpleChanges) {
93+
if (changes.columnDate) {
94+
this.columnDate$.next(changes.columnDate.currentValue);
95+
}
96+
}
97+
}

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

+15
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,16 @@ export interface CalendarWeekViewBeforeRenderEvent extends WeekView {
241241
class="cal-day-column"
242242
*ngFor="let column of view.hourColumns; trackBy: trackByHourColumn"
243243
>
244+
<mwl-calendar-week-view-current-time-marker
245+
[columnDate]="column.date"
246+
[dayStartHour]="dayStartHour"
247+
[dayStartMinute]="dayStartMinute"
248+
[dayEndHour]="dayEndHour"
249+
[dayEndMinute]="dayEndMinute"
250+
[hourSegments]="hourSegments"
251+
[hourSegmentHeight]="hourSegmentHeight"
252+
[customTemplate]="currentTimeMarkerTemplate"
253+
></mwl-calendar-week-view-current-time-marker>
244254
<div class="cal-events-container">
245255
<div
246256
*ngFor="
@@ -531,6 +541,11 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy {
531541
*/
532542
@Input() daysInWeek: number;
533543

544+
/**
545+
* A custom template to use for the current time marker
546+
*/
547+
@Input() currentTimeMarkerTemplate: TemplateRef<any>;
548+
534549
/**
535550
* Called when a header week day is clicked. Adding a `cssClass` property on `$event.day` will add that class to the header element
536551
*/

projects/angular-calendar/src/modules/week/calendar-week-view.scss

+11
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ $cal-week-view-vars: map-merge($cal-vars, $cal-week-view-vars);
7676
.cal-hour:last-child :not(:last-child) .cal-hour-segment {
7777
border-bottom-color: map-get($theme, border-color);
7878
}
79+
80+
.cal-current-time-marker {
81+
background-color: map-get($theme, current-time-marker-color);
82+
}
7983
}
8084
}
8185

@@ -124,6 +128,13 @@ $cal-week-view-vars: map-merge($cal-vars, $cal-week-view-vars);
124128
height: 100%;
125129
}
126130

131+
.cal-current-time-marker {
132+
position: absolute;
133+
width: 100%;
134+
height: 2px;
135+
z-index: 2;
136+
}
137+
127138
.cal-all-day-events {
128139
border: solid 1px;
129140
border-top: 0;

projects/angular-calendar/src/modules/week/calendar-week.module.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CalendarWeekViewHeaderComponent } from './calendar-week-view-header.com
77
import { CalendarWeekViewEventComponent } from './calendar-week-view-event.component';
88
import { CalendarCommonModule } from '../common/calendar-common.module';
99
import { CalendarWeekViewHourSegmentComponent } from './calendar-week-view-hour-segment.component';
10+
import { CalendarWeekViewCurrentTimeMarkerComponent } from './calendar-week-view-current-time-marker.component';
1011

1112
export {
1213
CalendarWeekViewComponent,
@@ -30,15 +31,17 @@ export { getWeekViewPeriod } from '../common/util';
3031
CalendarWeekViewComponent,
3132
CalendarWeekViewHeaderComponent,
3233
CalendarWeekViewEventComponent,
33-
CalendarWeekViewHourSegmentComponent
34+
CalendarWeekViewHourSegmentComponent,
35+
CalendarWeekViewCurrentTimeMarkerComponent
3436
],
3537
exports: [
3638
ResizableModule,
3739
DragAndDropModule,
3840
CalendarWeekViewComponent,
3941
CalendarWeekViewHeaderComponent,
4042
CalendarWeekViewEventComponent,
41-
CalendarWeekViewHourSegmentComponent
43+
CalendarWeekViewHourSegmentComponent,
44+
CalendarWeekViewCurrentTimeMarkerComponent
4245
]
4346
})
4447
export class CalendarWeekModule {}

projects/angular-calendar/src/variables.scss

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ $cal-bg-active: #ededed !default;
77
$cal-today-bg: #e8fde7 !default;
88
$cal-weekend-color: #8b0000 !default;
99
$cal-badge-color: #b94a48 !default;
10+
$cal-current-time-marker-color: #ea4334 !default;
1011
$cal-white: #fff !default;
1112
$cal-gray: #555 !default;
1213
$cal-black: #000 !default;
@@ -42,6 +43,9 @@ $cal-vars: map-merge(
4243
// the badge background color on the month view
4344
badge-color: $cal-badge-color,
4445

46+
// the current time marker color on the week and day view
47+
current-time-marker-color: $cal-current-time-marker-color,
48+
4549
// a standard white color used for tooltip text and month view event titles
4650
white: $cal-white,
4751

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

+16-16
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ describe('CalendarDayViewComponent component', () => {
106106
fixture.destroy();
107107
});
108108

109-
it('should call the event clicked callback', async(() => {
109+
it('should call the event clicked callback', () => {
110110
const fixture: ComponentFixture<
111111
CalendarDayViewComponent
112112
> = TestBed.createComponent(CalendarDayViewComponent);
@@ -123,16 +123,15 @@ describe('CalendarDayViewComponent component', () => {
123123
];
124124

125125
fixture.detectChanges();
126-
fixture.componentInstance.eventClicked.subscribe(val => {
127-
expect(val).to.deep.equal({
128-
event: fixture.componentInstance.events[0],
129-
sourceEvent: window['event']
130-
});
131-
});
126+
const spy = sinon.spy();
127+
fixture.componentInstance.eventClicked.subscribe(spy);
132128
fixture.nativeElement.querySelector('.cal-event-title').click();
133-
}));
129+
const call = spy.getCall(0).args[0];
130+
expect(call.event).to.equal(fixture.componentInstance.events[0]);
131+
expect(call.sourceEvent).to.be.an.instanceOf(MouseEvent);
132+
});
134133

135-
it('should call the event clicked callback on all day events', async(() => {
134+
it('should call the event clicked callback on all day events', () => {
136135
const fixture: ComponentFixture<
137136
CalendarDayViewComponent
138137
> = TestBed.createComponent(CalendarDayViewComponent);
@@ -150,14 +149,13 @@ describe('CalendarDayViewComponent component', () => {
150149
];
151150

152151
fixture.detectChanges();
153-
fixture.componentInstance.eventClicked.subscribe(val => {
154-
expect(val).to.deep.equal({
155-
event: fixture.componentInstance.events[0],
156-
sourceEvent: window['event']
157-
});
158-
});
152+
const spy = sinon.spy();
153+
fixture.componentInstance.eventClicked.subscribe(spy);
159154
fixture.nativeElement.querySelector('.cal-event-title').click();
160-
}));
155+
const call = spy.getCall(0).args[0];
156+
expect(call.event).to.equal(fixture.componentInstance.events[0]);
157+
expect(call.sourceEvent).to.be.an.instanceOf(MouseEvent);
158+
});
161159

162160
it('should add a custom CSS class to events', () => {
163161
const fixture: ComponentFixture<
@@ -743,6 +741,7 @@ describe('CalendarDayViewComponent component', () => {
743741
triggerDomEvent('mouseleave', event);
744742
fixture.detectChanges();
745743
expect(!!document.body.querySelector('.cal-tooltip')).to.equal(false);
744+
fixture.destroy();
746745
}));
747746

748747
it('should disable the tooltip', fakeAsync(() => {
@@ -771,6 +770,7 @@ describe('CalendarDayViewComponent component', () => {
771770
fixture.detectChanges();
772771
flush();
773772
expect(!!document.body.querySelector('.cal-tooltip')).to.equal(false);
773+
fixture.destroy();
774774
}));
775775

776776
it('should allow events to be dragged and dropped', () => {

0 commit comments

Comments
 (0)