qcal/frcal/src/Calendar.tsx

172 lines
5.5 KiB
TypeScript
Raw Normal View History

2022-02-12 13:02:31 -05:00
import React from 'react';
2022-02-12 14:55:46 -05:00
import './Calendar.scss';
2022-02-12 17:46:33 -05:00
import {
2022-02-19 21:24:42 -05:00
dateName,
dateRuralName,
2022-02-12 17:46:33 -05:00
decadeNames,
2023-04-21 02:21:12 -04:00
FrenchDay,
FrenchMonth,
frEndYear,
2022-02-12 17:46:33 -05:00
frIsLeap,
frJDN,
frStartYear,
2022-02-12 17:46:33 -05:00
jdnFrench,
2022-02-12 17:58:54 -05:00
monthName,
2023-04-21 02:21:12 -04:00
} from '@common/french';
2023-04-22 03:37:53 -04:00
import {jdnDate} from '@common/gregorian';
2023-04-21 02:21:12 -04:00
import {jdnLongCount} from '@common/longCount';
import {useMobileTooltipProps} from '@common/ui/MobileTooltip';
import {MonthBasedCalendar} from '@common/ui/MonthBasedCalendar';
type FrenchYear = number;
2022-02-12 13:02:31 -05:00
type MonthProps = {
year: FrenchYear;
2023-04-21 02:21:12 -04:00
month: FrenchMonth;
2022-02-12 13:02:31 -05:00
};
type DateProps = MonthProps & {
2023-04-21 02:21:12 -04:00
day: FrenchDay;
2022-02-12 13:02:31 -05:00
};
function DecadeName({name}: { name: string }): JSX.Element {
2022-02-12 13:27:14 -05:00
return <div className="DecadeName">{name}</div>;
2022-02-12 13:02:31 -05:00
}
function DayDetail({jdn}: { jdn: number }): JSX.Element {
return <div className="DayDetail">
2023-04-22 03:37:53 -04:00
<div className="DayDetail-gregorian">{jdnDate(jdn).toDateString()}</div>
2023-04-22 03:46:04 -04:00
<div className="DayDetail-lc">{jdnLongCount(jdn)?.join('.')}</div>
2022-02-12 13:02:31 -05:00
</div>;
}
2022-02-12 15:09:24 -05:00
function NormalDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element {
const jdn = frJDN(year, month, day);
2022-02-13 00:02:01 -05:00
const rural = dateRuralName(month, day)!;
const mobile = useMobileTooltipProps();
2022-02-12 15:09:24 -05:00
return <div className={`Day NormalDay ${jdn === todayJDN ? 'Day-today' : ''}`}>
2022-02-12 13:02:31 -05:00
<div className="Day-name">{day}</div>
2022-02-12 13:27:14 -05:00
<div className="Day-decade">{decadeNames[(day - 1) % 10]}</div>
<div className="Day-rural"><abbr title={rural.title} {...mobile}>{rural.name}</abbr></div>
2022-02-12 15:09:24 -05:00
<DayDetail jdn={jdn}/>
2022-02-12 13:27:14 -05:00
</div>;
2022-02-12 13:02:31 -05:00
}
2022-02-12 15:09:24 -05:00
function NormalMonth({year, month, todayJDN}: MonthProps & { todayJDN: number }): JSX.Element {
const decadeHeads = decadeNames.map((name, i) => <DecadeName key={i} name={name}/>);
2022-02-12 13:27:14 -05:00
return <div className="Month">
<div className="Month-decadeHead">{decadeHeads}</div>
<div className="Month-decades">{
Array.from(Array(3).keys()).map(i => <div key={i} className="Month-decade">{
Array.from(Array(10).keys()).map(j => <React.Fragment key={j}>
2023-04-21 02:21:12 -04:00
<NormalDay year={year} month={month} day={i * 10 + j + 1 as FrenchDay} todayJDN={todayJDN}/>
2022-02-13 15:00:20 -05:00
{j % 2 === 1 && <div className="Month-decadeSplitter-small"/>}
{j === 4 && <div className="Month-decadeSplitter-medium"/>}
</React.Fragment>)
2022-02-12 13:27:14 -05:00
}</div>)
}</div>
</div>;
2022-02-12 13:02:31 -05:00
}
function ComplementaryDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element {
const jdn = frJDN(year, month, day);
return <div className={`Day ComplementaryDay ${jdn === todayJDN ? 'Day-today' : ''}`}>
<div className="Day-name">{dateName(month, day)}</div>
<DayDetail jdn={jdn}/>
</div>;
}
function ComplementaryDays({year, todayJDN}: { year: FrenchYear, todayJDN: number }): JSX.Element {
2022-02-12 23:15:15 -05:00
const leap = frIsLeap(year);
return <div className="ComplementaryDays">{
Array.from(Array(6).keys()).map(i => <React.Fragment key={i}>
2023-04-21 02:21:12 -04:00
{(i < 5 || leap) && <ComplementaryDay year={year} month={13} day={i + 1 as FrenchDay} todayJDN={todayJDN}/>}
2022-02-12 23:15:15 -05:00
{i === 5 && !leap && <div className="ComplementaryDay-fake"/>}
{i % 2 === 1 && <div className="ComplementaryDays-splitter"/>}
</React.Fragment>)
}</div>;
}
export class Calendar extends MonthBasedCalendar<FrenchYear, FrenchMonth> {
override parseYear(year: string): FrenchYear {
return +year;
}
2022-02-12 17:46:33 -05:00
override parseMonth(month: string): FrenchMonth {
return +month as FrenchMonth;
2022-02-12 17:46:33 -05:00
}
override yearToString(year: FrenchYear): string {
return year.toString();
2022-02-12 17:46:33 -05:00
}
override monthToString(month: FrenchMonth): string {
return month.toString();
2022-02-12 17:46:33 -05:00
}
2022-02-12 15:40:45 -05:00
private goToNormalized(year: number, month: number) {
while (month < 1) {
2022-02-12 15:40:45 -05:00
--year;
month += 13;
}
while (month > 13) {
2022-02-12 15:40:45 -05:00
++year;
month -= 13;
}
2023-04-22 19:02:25 -04:00
if (year < frStartYear) {
year = frStartYear;
2022-02-12 17:58:54 -05:00
month = 1;
2023-04-22 19:02:25 -04:00
} else if (year > frEndYear) {
year = frEndYear;
2022-02-12 17:58:54 -05:00
month = 13;
}
this.goTo(year, month as FrenchMonth);
2022-02-12 15:40:45 -05:00
}
override prevYear = () => {
2022-02-12 15:40:45 -05:00
this.goToNormalized(this.props.year - 1, this.props.month);
};
2022-02-12 15:40:45 -05:00
override prevMonth = () => {
2022-02-12 15:40:45 -05:00
this.goToNormalized(this.props.year, this.props.month - 1);
};
2022-02-12 15:40:45 -05:00
override nextYear = () => {
2022-02-12 15:40:45 -05:00
this.goToNormalized(this.props.year + 1, this.props.month);
};
2022-02-12 15:40:45 -05:00
override nextMonth = () => {
2022-02-12 15:40:45 -05:00
this.goToNormalized(this.props.year, this.props.month + 1);
};
2022-02-12 17:46:33 -05:00
override isValidYear(year: string): boolean {
return /^-?\d+/.test(year);
2022-02-12 17:46:33 -05:00
}
override jdnLookup(jdn: number): { year: FrenchYear; month: FrenchMonth } {
return jdnFrench(jdn);
2022-02-12 17:46:33 -05:00
}
override monthName(year: FrenchYear, month: FrenchMonth): string {
return month === 13 ? year.toString() : `${monthName(month)} ${year}`;
2022-02-12 17:46:33 -05:00
}
override renderMonthOptions(): JSX.Element[] {
return Array.from(Array(13).keys()).map(i => {
const month = i + 1 as FrenchMonth;
return <option key={i} value={month}>{monthName(month)}</option>;
});
2022-02-19 21:24:42 -05:00
}
override renderBody(): JSX.Element {
if (this.props.month < 13) {
return <NormalMonth year={this.props.year} month={this.props.month} todayJDN={this.props.todayJDN}/>;
} else {
return <ComplementaryDays year={this.props.year} todayJDN={this.props.todayJDN}/>;
2022-02-12 17:46:33 -05:00
}
}
2022-02-12 13:02:31 -05:00
}