/** * 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 && ( )} {monthViewLayout === MonthLayoutEnum.VERTICAL && !yearView ? ( ) : ( {ShireWeekdays.map((weekday) => { const weekdayName = weekday[region]; return ( ); })} )} {yearView ? ( ) : monthViewLayout === MonthLayoutEnum.VERTICAL ? ( ) : ( )}
{caption === true ? "Shire Reckoning" : caption}
); }; 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;