166 lines
3.9 KiB
TypeScript
166 lines
3.9 KiB
TypeScript
import {
|
|
getMonthNames,
|
|
getRealDay,
|
|
getWeekDays,
|
|
getWeekNumber,
|
|
} from './dateutil';
|
|
|
|
export type Week = (Date | null)[];
|
|
|
|
export class Calendar {
|
|
public weeks?: Week[];
|
|
public monthNames = getMonthNames(this.locale);
|
|
public dayNames = getWeekDays(this.locale);
|
|
|
|
constructor(public currentDate: Date, private locale = 'en-US') {}
|
|
|
|
public setDate(date: Date) {
|
|
this.currentDate = date;
|
|
this.generateMonth();
|
|
}
|
|
|
|
public set year(year: number) {
|
|
this.currentDate.setFullYear(year);
|
|
this.generateMonth();
|
|
}
|
|
public get year() {
|
|
return this.currentDate.getFullYear();
|
|
}
|
|
|
|
public set month(month: number) {
|
|
this.currentDate.setMonth(month);
|
|
this.generateMonth();
|
|
}
|
|
public get month() {
|
|
return this.currentDate.getMonth();
|
|
}
|
|
|
|
public get monthName() {
|
|
return this.monthNames[this.currentDate.getMonth()];
|
|
}
|
|
|
|
public set day(day: number) {
|
|
this.currentDate.setDate(day);
|
|
this.generateMonth();
|
|
}
|
|
public get day() {
|
|
return this.currentDate.getDate();
|
|
}
|
|
|
|
public get date() {
|
|
return this.currentDate;
|
|
}
|
|
|
|
public get firstOfMonth() {
|
|
return new Date(
|
|
Date.UTC(this.currentDate.getFullYear(), this.currentDate.getMonth(), 1)
|
|
);
|
|
}
|
|
|
|
public get lastOfMonth() {
|
|
return new Date(
|
|
Date.UTC(
|
|
this.currentDate.getFullYear(),
|
|
this.currentDate.getMonth() + 1,
|
|
0
|
|
)
|
|
);
|
|
}
|
|
|
|
public get weekNumbers() {
|
|
return this.weeks!.map(([first]) => getWeekNumber(first!));
|
|
}
|
|
|
|
public get weekCount() {
|
|
return Math.ceil(
|
|
((this.firstOfMonth.getDay() || 7) - 1 + this.lastOfMonth.getDate()) / 7
|
|
);
|
|
}
|
|
|
|
public nextMonth() {
|
|
this.currentDate?.setMonth(this.currentDate.getMonth() + 1);
|
|
this.generateMonth();
|
|
}
|
|
|
|
public previousMonth() {
|
|
this.currentDate?.setMonth(this.currentDate.getMonth() - 1);
|
|
this.generateMonth();
|
|
}
|
|
|
|
public isOutOfMonth(date: Date) {
|
|
return date.getMonth() !== this.currentDate.getMonth();
|
|
}
|
|
|
|
public dateEquals(date: Date, target: Date) {
|
|
return (
|
|
date.getDate() === target.getDate() &&
|
|
date.getFullYear() === target.getFullYear() &&
|
|
date.getMonth() === target.getMonth()
|
|
);
|
|
}
|
|
|
|
public isToday(date: Date) {
|
|
const today = new Date();
|
|
return this.dateEquals(date, today);
|
|
}
|
|
|
|
public static populatePartialWeeks(weeks: Week[]) {
|
|
// This reducer fills the first and/or last week of the month
|
|
// with dates from previous/next month
|
|
weeks.forEach((week, weekIndex) => {
|
|
if (week.includes(null)) {
|
|
const dir = weekIndex === 0 ? -1 : 1;
|
|
const reducer = (
|
|
previous: Date | null,
|
|
current: Date | null,
|
|
index: number
|
|
) => {
|
|
if (!current && previous) {
|
|
week[index] = new Date(
|
|
Date.UTC(
|
|
previous.getFullYear(),
|
|
previous.getMonth(),
|
|
previous.getDate() + dir
|
|
)
|
|
);
|
|
}
|
|
|
|
return week[index];
|
|
};
|
|
|
|
const start = dir === -1 ? week[6] : week[0];
|
|
dir < 0
|
|
? week.reduceRight(reducer, start)
|
|
: week.reduce(reducer, start);
|
|
}
|
|
});
|
|
return weeks;
|
|
}
|
|
|
|
public generateMonth() {
|
|
const year = this.currentDate.getFullYear();
|
|
const month = this.currentDate.getMonth();
|
|
|
|
// Create week arrays, fill with nulls
|
|
const weeks = Array.from({ length: this.weekCount }, (k, i) =>
|
|
new Array(7).fill(null)
|
|
);
|
|
|
|
// Starting offset of the dates in this month
|
|
let offset = getRealDay(this.firstOfMonth);
|
|
|
|
// Create dates for each day of the week
|
|
for (let ci = 1; ci <= this.lastOfMonth.getDate(); ci++, offset++) {
|
|
const iterDate = new Date(Date.UTC(year, month, ci));
|
|
const weekOfMonth = Math.floor(offset / 7);
|
|
weeks[weekOfMonth][getRealDay(iterDate)] = iterDate;
|
|
}
|
|
|
|
// Populate dates that are in previous or next month
|
|
Calendar.populatePartialWeeks(weeks);
|
|
this.weeks = weeks;
|
|
|
|
return weeks;
|
|
}
|
|
}
|