react-trash-calendar/src/lib/calendar/calendar.ts

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;
}
}