From aa7edd9601686d3a6d87e07de68aa073e410e24f Mon Sep 17 00:00:00 2001 From: Matt Lewis Date: Sun, 5 Aug 2018 22:16:03 -0700 Subject: [PATCH] feat(week-view): make the week view title work with no config with i18n BREAKING CHANGE: the format of the week view title has changed from `Week d of yyyy` to `MMM d - MMM d, yyyy`. You can override this by using a custom date formatter. Closes #670 --- demos/demo-modules/exclude-days/component.ts | 10 ++- demos/demo-modules/exclude-days/template.html | 2 +- demos/demo-modules/i18n/component.ts | 15 +--- .../i18n/custom-date-formatter.provider.ts | 11 --- demos/demo-modules/i18n/sources.ts | 7 -- ...alendar-angular-date-formatter.provider.ts | 31 +++++-- .../calendar-date-formatter.interface.ts | 16 ++++ src/modules/common/calendar-date.pipe.ts | 17 +++- ...calendar-moment-date-formatter.provider.ts | 33 +++++-- ...calendar-native-date-formatter.provider.ts | 34 ++++++-- src/modules/common/util.ts | 33 +++++++ .../week/calendar-week-view.component.ts | 86 +++++++++---------- src/modules/week/calendar-week.module.ts | 1 + ...ar-angular-date-formatter.provider.spec.ts | 2 +- test/calendar-date.pipe.spec.ts | 26 ++++-- ...dar-moment-date-formatter.provider.spec.ts | 17 +++- ...dar-native-date-formatter.provider.spec.ts | 2 +- 17 files changed, 230 insertions(+), 113 deletions(-) delete mode 100644 demos/demo-modules/i18n/custom-date-formatter.provider.ts diff --git a/demos/demo-modules/exclude-days/component.ts b/demos/demo-modules/exclude-days/component.ts index 34eb893c4..1814cf01f 100644 --- a/demos/demo-modules/exclude-days/component.ts +++ b/demos/demo-modules/exclude-days/component.ts @@ -3,7 +3,7 @@ import { ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core'; -import { CalendarEvent } from 'angular-calendar'; +import { CalendarEvent, DAYS_OF_WEEK } from 'angular-calendar'; import { colors } from '../demo-utils/colors'; @Component({ @@ -22,15 +22,19 @@ export class DemoComponent { start: new Date('2016-01-08'), end: new Date('2016-01-10'), title: 'One day excluded event', - color: colors.red + color: colors.red, + allDay: true }, { start: new Date('2016-01-01'), end: new Date('2016-01-09'), - title: 'Multiple weeks event' + title: 'Multiple weeks event', + allDay: true } ]; // exclude weekends excludeDays: number[] = [0, 6]; + + weekStartsOn = DAYS_OF_WEEK.SUNDAY; } diff --git a/demos/demo-modules/exclude-days/template.html b/demos/demo-modules/exclude-days/template.html index 7700b6b91..9470b7eea 100644 --- a/demos/demo-modules/exclude-days/template.html +++ b/demos/demo-modules/exclude-days/template.html @@ -26,7 +26,7 @@
-

{{ viewDate | calendarDate:(view + 'ViewTitle'):'en' }}

+

{{ viewDate | calendarDate:(view + 'ViewTitle'):'en':weekStartsOn:excludeDays }}

diff --git a/demos/demo-modules/i18n/component.ts b/demos/demo-modules/i18n/component.ts index 412714988..84d168af1 100644 --- a/demos/demo-modules/i18n/component.ts +++ b/demos/demo-modules/i18n/component.ts @@ -1,21 +1,10 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; -import { - CalendarEvent, - CalendarDateFormatter, - DAYS_OF_WEEK -} from 'angular-calendar'; -import { CustomDateFormatter } from './custom-date-formatter.provider'; +import { CalendarEvent, DAYS_OF_WEEK } from 'angular-calendar'; @Component({ selector: 'mwl-demo-component', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: 'template.html', - providers: [ - { - provide: CalendarDateFormatter, - useClass: CustomDateFormatter - } - ] + templateUrl: 'template.html' }) export class DemoComponent { view: string = 'month'; diff --git a/demos/demo-modules/i18n/custom-date-formatter.provider.ts b/demos/demo-modules/i18n/custom-date-formatter.provider.ts deleted file mode 100644 index 4dd6667ab..000000000 --- a/demos/demo-modules/i18n/custom-date-formatter.provider.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CalendarDateFormatter, DateFormatterParams } from 'angular-calendar'; -import { getISOWeek } from 'date-fns'; -import { DatePipe } from '@angular/common'; - -export class CustomDateFormatter extends CalendarDateFormatter { - public weekViewTitle({ date, locale }: DateFormatterParams): string { - const year: string = new DatePipe(locale).transform(date, 'y', locale); - const weekNumber: number = getISOWeek(date); - return `Semaine ${weekNumber} en ${year}`; - } -} diff --git a/demos/demo-modules/i18n/sources.ts b/demos/demo-modules/i18n/sources.ts index ed3bf19be..f6e7fbf15 100644 --- a/demos/demo-modules/i18n/sources.ts +++ b/demos/demo-modules/i18n/sources.ts @@ -6,13 +6,6 @@ export const sources = [ highlighted: require('!!raw-loader!highlightjs-loader?lang=typescript!./component') } }, - { - filename: 'custom-date-formatter.provider.ts', - contents: { - raw: require('!!raw-loader!./custom-date-formatter.provider'), - highlighted: require('!!raw-loader!highlightjs-loader?lang=typescript!./custom-date-formatter.provider') - } - }, { filename: 'template.html', contents: { diff --git a/src/modules/common/calendar-angular-date-formatter.provider.ts b/src/modules/common/calendar-angular-date-formatter.provider.ts index 9104613d7..271693b12 100644 --- a/src/modules/common/calendar-angular-date-formatter.provider.ts +++ b/src/modules/common/calendar-angular-date-formatter.provider.ts @@ -5,6 +5,7 @@ import { import { DatePipe } from '@angular/common'; import { Injectable } from '@angular/core'; import { DateAdapter } from '../../date-adapters/date-adapter'; +import { getWeekViewPeriod } from './util'; /** * This will use the angular date pipe to do all date formatting. It is the default date formatter used by the calendar. @@ -55,15 +56,31 @@ export class CalendarAngularDateFormatter /** * The week view title */ - public weekViewTitle({ date, locale }: DateFormatterParams): string { - const year: string = new DatePipe(locale).transform( + public weekViewTitle({ + date, + locale, + weekStartsOn, + excludeDays, + daysInWeek + }: DateFormatterParams): string { + const { viewStart, viewEnd } = getWeekViewPeriod( + this.dateAdapter, date, - 'y', - null, - locale + weekStartsOn, + excludeDays, + daysInWeek ); - const weekNumber: number = this.dateAdapter.getISOWeek(date); - return `Week ${weekNumber} of ${year}`; + const format = (dateToFormat: Date, showYear: boolean) => + new DatePipe(locale).transform( + dateToFormat, + 'MMM d' + (showYear ? ', yyyy' : ''), + null, + locale + ); + return `${format( + viewStart, + viewStart.getUTCFullYear() !== viewEnd.getUTCFullYear() + )} - ${format(viewEnd, true)}`; } /** diff --git a/src/modules/common/calendar-date-formatter.interface.ts b/src/modules/common/calendar-date-formatter.interface.ts index 559dd5f2f..6b43fb207 100644 --- a/src/modules/common/calendar-date-formatter.interface.ts +++ b/src/modules/common/calendar-date-formatter.interface.ts @@ -11,6 +11,22 @@ export interface DateFormatterParams { * The users preferred locale. */ locale?: string; + + /** + * The start day number of the week + */ + weekStartsOn?: number; + + /** + * An array of day indexes (0 = sunday, 1 = monday etc) that will be hidden on the view + */ + excludeDays?: number[]; + + /** + * The number of days in a week. Can be used to create a shorter or longer week view. + * The first day of the week will always be the `viewDate` + */ + daysInWeek?: number; } /** diff --git a/src/modules/common/calendar-date.pipe.ts b/src/modules/common/calendar-date.pipe.ts index 2ced57d87..7947a4948 100644 --- a/src/modules/common/calendar-date.pipe.ts +++ b/src/modules/common/calendar-date.pipe.ts @@ -17,7 +17,20 @@ export class CalendarDatePipe implements PipeTransform { @Inject(LOCALE_ID) private locale: string ) {} - transform(date: Date, method: string, locale: string = this.locale): string { - return this.dateFormatter[method]({ date, locale }); + transform( + date: Date, + method: string, + locale: string = this.locale, + weekStartsOn: number = 0, + excludeDays: number[] = [], + daysInWeek?: number + ): string { + return this.dateFormatter[method]({ + date, + locale, + weekStartsOn, + excludeDays, + daysInWeek + }); } } diff --git a/src/modules/common/calendar-moment-date-formatter.provider.ts b/src/modules/common/calendar-moment-date-formatter.provider.ts index c50882b9e..cd8cf9d90 100644 --- a/src/modules/common/calendar-moment-date-formatter.provider.ts +++ b/src/modules/common/calendar-moment-date-formatter.provider.ts @@ -3,6 +3,8 @@ import { CalendarDateFormatterInterface, DateFormatterParams } from './calendar-date-formatter.interface'; +import { getWeekViewPeriod } from './util'; +import { DateAdapter } from '../../date-adapters/date-adapter'; export const MOMENT: InjectionToken = new InjectionToken('Moment'); @@ -27,7 +29,10 @@ export class CalendarMomentDateFormatter /** * @hidden */ - constructor(@Inject(MOMENT) private moment: any) {} + constructor( + @Inject(MOMENT) private moment: any, + private dateAdapter: DateAdapter + ) {} /** * The month view header week day labels @@ -80,10 +85,28 @@ export class CalendarMomentDateFormatter /** * The week view title */ - public weekViewTitle({ date, locale }: DateFormatterParams): string { - return this.moment(date) - .locale(locale) - .format('[Week] W [of] YYYY'); + public weekViewTitle({ + date, + locale, + weekStartsOn, + excludeDays, + daysInWeek + }: DateFormatterParams): string { + const { viewStart, viewEnd } = getWeekViewPeriod( + this.dateAdapter, + date, + weekStartsOn, + excludeDays, + daysInWeek + ); + const format = (dateToFormat: Date, showYear: boolean) => + this.moment(dateToFormat) + .locale(locale) + .format('MMM D' + (showYear ? ', YYYY' : '')); + return `${format( + viewStart, + viewStart.getUTCFullYear() !== viewEnd.getUTCFullYear() + )} - ${format(viewEnd, true)}`; } /** diff --git a/src/modules/common/calendar-native-date-formatter.provider.ts b/src/modules/common/calendar-native-date-formatter.provider.ts index 0faa076f6..5875835e4 100644 --- a/src/modules/common/calendar-native-date-formatter.provider.ts +++ b/src/modules/common/calendar-native-date-formatter.provider.ts @@ -4,6 +4,8 @@ import { } from './calendar-date-formatter.interface'; import { Injectable } from '@angular/core'; import { DateAdapter } from '../../date-adapters/date-adapter'; +import { getWeekViewPeriod } from './util'; +import show = Mocha.reporters.Base.cursor.show; /** * This will use Intl API to do all date formatting. @@ -62,12 +64,32 @@ export class CalendarNativeDateFormatter /** * The week view title */ - public weekViewTitle({ date, locale }: DateFormatterParams): string { - const year: string = new Intl.DateTimeFormat(locale, { - year: 'numeric' - }).format(date); - const weekNumber: number = this.dateAdapter.getISOWeek(date); - return `Week ${weekNumber} of ${year}`; + public weekViewTitle({ + date, + locale, + weekStartsOn, + excludeDays, + daysInWeek + }: DateFormatterParams): string { + const { viewStart, viewEnd } = getWeekViewPeriod( + this.dateAdapter, + date, + weekStartsOn, + excludeDays, + daysInWeek + ); + + const format = (dateToFormat: Date, showYear: boolean) => + new Intl.DateTimeFormat(locale, { + day: 'numeric', + month: 'short', + year: showYear ? 'numeric' : undefined + }).format(dateToFormat); + + return `${format( + viewStart, + viewStart.getUTCFullYear() !== viewEnd.getUTCFullYear() + )} - ${format(viewEnd, true)}`; } /** diff --git a/src/modules/common/util.ts b/src/modules/common/util.ts index 1712b623d..4e913fc83 100644 --- a/src/modules/common/util.ts +++ b/src/modules/common/util.ts @@ -136,3 +136,36 @@ export function shouldFireDroppedEvent( (!dropEvent.dropData.event.allDay && allDay)) ); } + +export function getWeekViewPeriod( + dateAdapter: DateAdapter, + viewDate: Date, + weekStartsOn: number, + excluded: number[] = [], + daysInWeek?: number +): { viewStart: Date; viewEnd: Date } { + let viewStart = daysInWeek + ? dateAdapter.startOfDay(viewDate) + : dateAdapter.startOfWeek(viewDate, { weekStartsOn }); + if (excluded.indexOf(dateAdapter.getDay(viewStart)) > -1) { + viewStart = dateAdapter.subDays( + addDaysWithExclusions(dateAdapter, viewStart, 1, excluded), + 1 + ); + } + if (daysInWeek) { + const viewEnd = dateAdapter.endOfDay( + addDaysWithExclusions(dateAdapter, viewStart, daysInWeek - 1, excluded) + ); + return { viewStart, viewEnd }; + } else { + let viewEnd = dateAdapter.endOfWeek(viewDate, { weekStartsOn }); + if (excluded.indexOf(dateAdapter.getDay(viewEnd)) > -1) { + viewEnd = dateAdapter.addDays( + addDaysWithExclusions(dateAdapter, viewEnd, -1, excluded), + 1 + ); + } + return { viewStart, viewEnd }; + } +} diff --git a/src/modules/week/calendar-week-view.component.ts b/src/modules/week/calendar-week-view.component.ts index ff4d4bf20..d9c7e3f36 100644 --- a/src/modules/week/calendar-week-view.component.ts +++ b/src/modules/week/calendar-week-view.component.ts @@ -44,7 +44,8 @@ import { addDaysWithExclusions, trackByDayOrWeekEvent, isDraggedWithinPeriod, - shouldFireDroppedEvent + shouldFireDroppedEvent, + getWeekViewPeriod } from '../common/util'; import { DateAdapter } from '../../date-adapters/date-adapter'; import { @@ -886,14 +887,19 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy { } private refreshHeader(): void { - this.days = this.utils.getWeekViewHeader( - this.adjustDaysInWeek({ - viewDate: this.viewDate, - weekStartsOn: this.weekStartsOn, - excluded: this.excludeDays, - weekendDays: this.weekendDays - }) - ); + this.days = this.utils.getWeekViewHeader({ + viewDate: this.viewDate, + weekStartsOn: this.weekStartsOn, + excluded: this.excludeDays, + weekendDays: this.weekendDays, + ...getWeekViewPeriod( + this.dateAdapter, + this.viewDate, + this.weekStartsOn, + this.excludeDays, + this.daysInWeek + ) + }); this.emitBeforeViewRender(); } @@ -917,27 +923,32 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy { } private getWeekView(events: CalendarEvent[]) { - return this.utils.getWeekView( - this.adjustDaysInWeek({ - events, - viewDate: this.viewDate, - weekStartsOn: this.weekStartsOn, - excluded: this.excludeDays, - precision: this.precision, - absolutePositionedEvents: true, - hourSegments: this.hourSegments, - dayStart: { - hour: this.dayStartHour, - minute: this.dayStartMinute - }, - dayEnd: { - hour: this.dayEndHour, - minute: this.dayEndMinute - }, - segmentHeight: this.hourSegmentHeight, - weekendDays: this.weekendDays - }) - ); + return this.utils.getWeekView({ + events, + viewDate: this.viewDate, + weekStartsOn: this.weekStartsOn, + excluded: this.excludeDays, + precision: this.precision, + absolutePositionedEvents: true, + hourSegments: this.hourSegments, + dayStart: { + hour: this.dayStartHour, + minute: this.dayStartMinute + }, + dayEnd: { + hour: this.dayEndHour, + minute: this.dayEndMinute + }, + segmentHeight: this.hourSegmentHeight, + weekendDays: this.weekendDays, + ...getWeekViewPeriod( + this.dateAdapter, + this.viewDate, + this.weekStartsOn, + this.excludeDays, + this.daysInWeek + ) + }); } private getDragMovedEventTimes( @@ -1092,19 +1103,4 @@ export class CalendarWeekViewComponent implements OnChanges, OnInit, OnDestroy { return newEventDates; } - - private adjustDaysInWeek(args: any): any { - if (this.daysInWeek) { - args.viewStart = this.dateAdapter.startOfDay(args.viewDate); - args.viewEnd = this.dateAdapter.endOfDay( - addDaysWithExclusions( - this.dateAdapter, - args.viewStart, - this.daysInWeek - 1, - this.excludeDays - ) - ); - } - return args; - } } diff --git a/src/modules/week/calendar-week.module.ts b/src/modules/week/calendar-week.module.ts index a615bf030..b2d19ad6e 100644 --- a/src/modules/week/calendar-week.module.ts +++ b/src/modules/week/calendar-week.module.ts @@ -17,6 +17,7 @@ export { WeekViewAllDayEventRow as CalendarWeekViewAllDayEventRow, GetWeekViewArgs as CalendarGetWeekViewArgs } from 'calendar-utils'; +export { getWeekViewPeriod } from '../common/util'; @NgModule({ imports: [ diff --git a/test/calendar-angular-date-formatter.provider.spec.ts b/test/calendar-angular-date-formatter.provider.spec.ts index 29266905a..435226511 100644 --- a/test/calendar-angular-date-formatter.provider.spec.ts +++ b/test/calendar-angular-date-formatter.provider.spec.ts @@ -73,7 +73,7 @@ describe('CalendarAngularDateFormatter provider', () => { date: new Date('2016-01-04'), locale: 'en' }) - ).to.equal('Week 1 of 2016'); + ).to.equal('Jan 3 - Jan 9, 2016'); }); it('weekViewHour', () => { diff --git a/test/calendar-date.pipe.spec.ts b/test/calendar-date.pipe.spec.ts index c8e014975..34c20090e 100644 --- a/test/calendar-date.pipe.spec.ts +++ b/test/calendar-date.pipe.spec.ts @@ -13,14 +13,18 @@ import { adapterFactory } from '../src/date-adapters/date-fns'; import { DateAdapter } from 'angular-calendar'; @Component({ - template: '{{ date | calendarDate:method:locale }}' + template: + '{{ date | calendarDate:method:locale:weekStartsOn:excludeDays:daysInWeek }}' }) class TestComponent { - public date: Date; - public view: string; - public format: string; - public locale: string; - public method: string; + date: Date; + view: string; + format: string; + locale: string; + method: string; + daysInWeek: number; + excludeDays: number[]; + weekStartsOn: number; } describe('calendarDate pipe', () => { @@ -66,7 +70,10 @@ describe('calendarDate pipe', () => { expect(fixture.nativeElement.innerHTML).to.equal('Friday'); expect(dateFormatter.monthViewColumnHeader).to.have.been.calledWith({ date: fixture.componentInstance.date, - locale: defaultLocale + locale: defaultLocale, + daysInWeek: undefined, + excludeDays: [], + weekStartsOn: 0 }); }); @@ -82,7 +89,10 @@ describe('calendarDate pipe', () => { expect(fixture.nativeElement.innerHTML).to.equal('Freitag'); expect(dateFormatter.monthViewColumnHeader).to.have.been.calledWith({ date: fixture.componentInstance.date, - locale: 'de' + locale: 'de', + daysInWeek: undefined, + excludeDays: [], + weekStartsOn: 0 }); }); }); diff --git a/test/calendar-moment-date-formatter.provider.spec.ts b/test/calendar-moment-date-formatter.provider.spec.ts index 83fcd09c5..102797f19 100644 --- a/test/calendar-moment-date-formatter.provider.spec.ts +++ b/test/calendar-moment-date-formatter.provider.spec.ts @@ -2,14 +2,25 @@ import { inject, TestBed } from '@angular/core/testing'; import { expect } from 'chai'; import moment from 'moment'; import { startOfDay } from 'date-fns'; -import { CalendarMomentDateFormatter, MOMENT } from '../src'; +import { + CalendarMomentDateFormatter, + MOMENT, + CalendarNativeDateFormatter, + DateAdapter +} from '../src'; +import { adapterFactory } from '../src/date-adapters/date-fns'; describe('calendarMomentDateFormatter provider', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ CalendarMomentDateFormatter, - { provide: MOMENT, useValue: moment } + { provide: MOMENT, useValue: moment }, + CalendarNativeDateFormatter, + { + provide: DateAdapter, + useFactory: adapterFactory + } ] }); }); @@ -70,7 +81,7 @@ describe('calendarMomentDateFormatter provider', () => { date: new Date('2016-01-04'), locale: 'en' }) - ).to.equal('Week 1 of 2016'); + ).to.equal('Jan 3 - Jan 9, 2016'); }); it('weekViewHour', () => { diff --git a/test/calendar-native-date-formatter.provider.spec.ts b/test/calendar-native-date-formatter.provider.spec.ts index 14554ce7a..116f97a52 100644 --- a/test/calendar-native-date-formatter.provider.spec.ts +++ b/test/calendar-native-date-formatter.provider.spec.ts @@ -73,7 +73,7 @@ describe('calendarNativeDateFormatter provider', () => { date: new Date('2016-01-04'), locale: 'en' }) - ).to.equal('Week 1 of 2016'); + ).to.equal('Jan 3 - Jan 9, 2016'); }); it('weekViewHour', () => {