qcal/frcal/src/App.tsx

148 lines
4.7 KiB
TypeScript
Raw Normal View History

2022-02-14 01:48:09 -05:00
import React, {FormEvent} from 'react';
2022-02-12 13:02:31 -05:00
import {Calendar} from './Calendar';
import {
endGregorian,
2023-04-22 19:02:25 -04:00
frEndJD,
2023-04-21 02:21:12 -04:00
FrenchMonth,
frSupportedYear,
jdnFrench,
startGregorian,
2023-04-22 19:02:25 -04:00
frStartJD,
2023-04-21 02:21:12 -04:00
} from '@common/french';
import {dateJDN, gregorianJDN} from '@common/gregorian';
2022-02-27 03:13:42 -05:00
import {TimeOfDay} from './TimeOfDay';
2022-02-12 16:25:44 -05:00
type YearMonth = {
year: number;
2023-04-21 02:21:12 -04:00
month: FrenchMonth;
2022-02-12 16:25:44 -05:00
}
function parseURL(): YearMonth | null {
const match = /\/(-?\d+)\/(\d+)/.exec(window.location.pathname);
if (!match)
return null;
const month = +match[2];
2022-02-12 17:58:54 -05:00
const year = +match[1];
if (!frSupportedYear(year) || month < 1 || month > 13)
2022-02-12 16:25:44 -05:00
return null;
2023-04-21 02:21:12 -04:00
return {year: year, month: month as FrenchMonth};
2022-02-12 16:25:44 -05:00
}
type AppState = YearMonth & {
todayJDN: number,
2022-02-14 01:48:09 -05:00
goYear: string,
goMonth: string,
goDay: string,
2022-02-12 16:25:44 -05:00
};
class App extends React.Component<{}, AppState> {
state: AppState;
constructor(props: {}) {
super(props);
const today = new Date();
const todayJDN = dateJDN(today);
const {year, month} = jdnFrench(todayJDN);
2022-02-12 16:25:44 -05:00
this.state = {
...(parseURL() || {year, month}),
todayJDN,
2022-02-14 01:48:09 -05:00
goYear: today.getFullYear().toString(),
goMonth: (today.getMonth() + 1).toString(),
goDay: today.getDate().toString(),
2022-02-12 16:25:44 -05:00
};
this.updateStateFromURL = this.updateStateFromURL.bind(this);
}
componentDidMount() {
window.addEventListener('popstate', this.updateStateFromURL);
}
componentWillUnmount() {
window.removeEventListener('popstate', this.updateStateFromURL);
}
private updateStateFromURL(event: PopStateEvent) {
this.setState(event.state);
}
private updateURL() {
const {year, month} = this.state;
const path = `/${year}/${month}`;
if (path !== window.location.pathname) {
window.history.pushState({year, month}, '', path);
}
}
2022-02-14 01:48:09 -05:00
changeField(field: keyof AppState, event: any) {
2022-02-19 21:24:42 -05:00
this.setState({[field]: event.target.value});
2022-02-14 01:48:09 -05:00
}
validYear() {
2023-04-22 03:37:53 -04:00
return /^-?\d+$/.test(this.state.goYear) && startGregorian[0] <= +this.state.goYear &&
+this.state.goYear <= endGregorian[0];
2022-02-14 01:48:09 -05:00
}
validMonth() {
return /^\d+$/.test(this.state.goMonth) && 1 <= +this.state.goMonth && +this.state.goMonth <= 12;
}
validDay() {
return /^\d+$/.test(this.state.goDay) && 1 <= +this.state.goDay && +this.state.goDay <= 31;
}
goToGregorian(event: FormEvent) {
event.preventDefault();
if (!this.validYear() || !this.validMonth() || !this.validDay())
return;
const jdn = gregorianJDN(+this.state.goYear, +this.state.goMonth, +this.state.goDay);
2023-04-22 19:02:25 -04:00
const {year, month} = jdnFrench(Math.min(Math.max(frStartJD, jdn), frEndJD));
2022-02-14 01:48:09 -05:00
this.setState({year, month});
}
2022-02-12 16:25:44 -05:00
setState(state: any, callback?: () => void) {
super.setState(state, () => {
this.updateURL();
callback && callback();
});
}
2022-02-27 03:13:42 -05:00
onDateChange = (todayJDN: number) => {
this.setState({todayJDN});
2023-04-22 03:37:53 -04:00
};
2022-02-27 03:13:42 -05:00
2022-02-12 16:25:44 -05:00
render() {
2022-02-14 01:48:09 -05:00
return <>
<Calendar
year={this.state.year} month={this.state.month} todayJDN={this.state.todayJDN}
onSwitch={(year, month) => {
this.setState({year, month});
}}/>
2022-02-27 03:13:42 -05:00
<TimeOfDay onDateChange={this.onDateChange}/>
2022-02-14 01:48:09 -05:00
<div className="navigate">
<h4>Go to a date</h4>
<form className="input-group" onSubmit={this.goToGregorian.bind(this)}>
<span className="input-group-text">Gregorian<span className="hide-small">&nbsp;Date</span></span>
<input type="number" className={`form-control go-year ${this.validYear() ? '' : 'is-invalid'}`}
onChange={this.changeField.bind(this, 'goYear')} value={this.state.goYear}
2023-04-22 03:37:53 -04:00
min={startGregorian[0]} max={endGregorian[0]}/>
2022-02-14 01:48:09 -05:00
<input type="number" className={`form-control go-month ${this.validMonth() ? '' : 'is-invalid'}`}
onChange={this.changeField.bind(this, 'goMonth')} value={this.state.goMonth}
min={1} max={12}/>
<input type="number" className={`form-control go-day ${this.validDay() ? '' : 'is-invalid'}`}
onChange={this.changeField.bind(this, 'goDay')} value={this.state.goDay}
min={1} max={31}/>
<button type="submit" className="form-control btn btn-primary go-button">Go</button>
</form>
</div>
</>;
2022-02-12 16:25:44 -05:00
}
}
export default App;