/**
* Copyright (C) Paul Sarando
* Distributed under the Eclipse Public License (http://www.eclipse.org/legal/epl-v10.html).
*/
import React from "react";
import {
ShireCalendarYear,
ShireDate,
ShireWeekdays,
ShireMonths,
ShireRegionEnum,
makeShireCalendarDates,
} from "../ShireReckoning";
import { GondorLeapYearRuleEnum } from "../GondorReckoning";
import { fullYearDate, datesMatch } from "../Utils";
import DateCell, { dateKey } from "./DateCell";
import IntercalaryDay from "./IntercalaryDay";
import WeekDayHeaderCell, {
addMonthFiller,
addVerticalMonthFiller,
} from "./WeekDayHeaderCell";
import "./tolkien-calendars.css";
import {
MonthLayoutEnum,
VerticalLayoutFiller,
} from "./controls/MonthViewLayout";
interface ShireCalendarProps {
caption?: string | boolean;
className?: string;
region?: ShireRegionEnum;
monthView?: number;
monthViewLayout?: MonthLayoutEnum;
startDay?: number;
yearView?: boolean;
date?: Date;
startDate?: Date;
calendarRules?: GondorLeapYearRuleEnum;
calendar?: ShireCalendarYear;
}
interface ShireDateProps {
dates: ShireDate[];
today: Date;
region: ShireRegionEnum;
}
interface ShireMonthProps extends ShireDateProps {
monthView: number;
}
const getDateColor = (
region: ShireRegionEnum,
date: ShireDate,
monthColor: string
): string => {
if (date.className !== undefined) {
return date.className;
}
const isHoliday =
region !== ShireRegionEnum.BREE
&& ((date.month === 3 && date.day === 6)
|| (date.month === 10 && date.day === 2));
return isHoliday ? "holiday" : monthColor;
};
const ShireDateCell = ({ dates, today, region }: ShireDateProps) => {
const date = dates[0];
const dayName = date.region ? date.region[region] : date.day;
let dayClassName;
let dayExtraClassName;
let dayExtra;
let gregorianExtra;
let description;
if (dates.length > 1) {
dayExtra = dates[1].region ? dates[1].region[region] : dates[1].day;
gregorianExtra = dates[1].gregorian;
}
switch (date.day) {
case "1 Yule":
return (
);
case "2 Yule":
return (
);
case "1 Lithe":
description = "Midsummer's Eve";
if (dayExtra === "Midyear's Day") {
description = "Midsummer's Eve and Midsummer Day!";
dayExtraClassName = "intercalary-midyears-day";
} else if (dayExtra) {
description = "Midsummer's Eve and Shire Leap Day!";
dayExtraClassName = "intercalary-overlithe-day";
}
return (
);
case "Midyear's Day":
return (
);
case "Overlithe":
description = "Shire Leap Day!";
if (dayExtra) {
dayClassName = "intercalary-overlithe-day";
description = "Shire Leap Day and Day after Midsummer.";
}
return (
);
case "2 Lithe":
return (
);
default:
const month = ShireMonths[date.month];
const weekday = ShireWeekdays[date.weekDay];
const className = getDateColor(region, date, month.className);
return (
);
}
};
const ShireMonth = ({ monthView, today, dates, region }: ShireMonthProps) => {
const weeks: React.JSX.Element[] = [];
let week: React.JSX.Element[] = [];
let i = 0,
date = dates[i],
overlithe = 1;
for (; i < dates.length && date.month < monthView; i++, date = dates[i]) {
// seek ahead to current month view
}
addMonthFiller(week, date.weekDay);
for (; i < dates.length && monthView === date.month; i++, date = dates[i]) {
const nextDates = [date];
if (i + 1 < dates.length && date.weekDay === dates[i + 1].weekDay) {
nextDates.push(dates[++i]);
}
week.push(
);
if (date.weekDay === 6) {
weeks.push(
{week}
);
week = [];
}
}
// Check if simulating without Shire Reform
if (monthView === 5 && date.day === "1 Lithe") {
for (
date = dates[i];
date.day === "1 Lithe"
|| date.day === "Midyear's Day"
|| date.day === "Overlithe"
|| date.day === "2 Lithe";
i++, date = dates[i]
) {
week.push(
);
if (date.weekDay === 6) {
weeks.push({week}
);
week = [];
}
}
}
if (week.length > 0) {
weeks.push({week}
);
}
return weeks;
};
const ShireMonthVertical = ({
monthView,
today,
dates,
region,
}: ShireMonthProps) => {
let weeks = ShireWeekdays.map(function (weekday) {
const weekdayName = weekday[region];
return [
,
];
});
let i = 0,
date = dates[i],
overlithe = 1;
for (; i < dates.length && date.month < monthView; i++, date = dates[i]) {
// seek ahead to current month view
}
addVerticalMonthFiller(weeks, date.weekDay);
for (; i < dates.length && monthView === date.month; i++, date = dates[i]) {
const nextDates = [date];
if (i + 1 < dates.length && date.weekDay === dates[i + 1].weekDay) {
nextDates.push(dates[++i]);
}
weeks[date.weekDay].push(
);
}
// Check if simulating without Shire Reform
if (monthView === 5 && date.day === "1 Lithe") {
for (
date = dates[i];
date.day === "1 Lithe"
|| date.day === "Midyear's Day"
|| date.day === "Overlithe"
|| date.day === "2 Lithe";
i++, date = dates[i]
) {
weeks[date.weekDay].push(
);
}
}
if (weeks[0].length > 6) {
weeks = ShireWeekdays.map(function (weekday, i) {
const week = weeks[i];
const weekdayName = weekday[region];
week.shift();
week.unshift(
);
return week;
});
}
return weeks.map(function (week, i) {
return {week}
;
});
};
const ShireYear = ({ today, dates, region }: ShireDateProps) => {
const weeks: React.JSX.Element[] = [];
let week: React.JSX.Element[] = [],
overlithe = 1;
addMonthFiller(week, dates[0].weekDay);
for (let i = 0, date = dates[i]; i < dates.length; i++, date = dates[i]) {
const nextDates = [date];
if (i + 1 < dates.length && date.weekDay === dates[i + 1].weekDay) {
nextDates.push(dates[++i]);
}
week.push(
);
if (date.weekDay === 6) {
weeks.push({week}
);
week = [];
}
}
if (week.length > 0) {
weeks.push({week}
);
}
return weeks;
};
const ShireCalendar = (props: ShireCalendarProps) => {
const {
caption,
className,
region = ShireRegionEnum.SHIRE,
monthViewLayout = MonthLayoutEnum.VERTICAL,
startDay = 21,
yearView = false,
} = props;
const nextDate = props.date || new Date();
const [today, setToday] = React.useState(nextDate);
const nextStartDate = props.startDate || fullYearDate(0, 11, startDay);
const [startDate, setStartDate] = React.useState(nextStartDate);
const nextRules = props.calendarRules || GondorLeapYearRuleEnum.GREGORIAN;
const [calendarRules, setCalendarRules] = React.useState(nextRules);
const [calendar, setCalendar] = React.useState(
() =>
props.calendar
|| makeShireCalendarDates(today, startDate, calendarRules)
);
const updateToday = !datesMatch(today, nextDate);
if (updateToday) {
setToday(nextDate);
}
const updateStartDate = !datesMatch(startDate, nextStartDate);
if (updateStartDate) {
setStartDate(nextStartDate);
}
const updateRules = calendarRules !== nextRules;
if (updateRules) {
setCalendarRules(nextRules);
}
const updateCalendar = props.calendar && props.calendar !== calendar;
if (updateCalendar || updateToday || updateStartDate || updateRules) {
setCalendar(
props.calendar
|| makeShireCalendarDates(nextDate, nextStartDate, nextRules)
);
}
const { dates, todayShire } = calendar;
const monthView =
props.monthView === undefined ? todayShire.month : props.monthView;
return (
{caption && (
{caption === true ? "Shire Reckoning" : caption}
)}
{monthViewLayout === MonthLayoutEnum.VERTICAL && !yearView ? (
) : (
{ShireWeekdays.map((weekday) => {
const weekdayName = weekday[region];
return (
);
})}
)}
{yearView ? (
) : monthViewLayout === MonthLayoutEnum.VERTICAL ? (
) : (
)}
);
};
ShireCalendar.REGION_NAMES_TOLKIEN = ShireRegionEnum.TOLKIEN;
ShireCalendar.REGION_NAMES_SHIRE = ShireRegionEnum.SHIRE;
ShireCalendar.REGION_NAMES_BREE = ShireRegionEnum.BREE;
ShireCalendar.MONTH_VIEW_VERTICAL = MonthLayoutEnum.VERTICAL;
ShireCalendar.MONTH_VIEW_HORIZONTAL = MonthLayoutEnum.HORIZONTAL;
export default ShireCalendar;