2023-07-15 15:37:25 -04:00
|
|
|
import React from 'react';
|
2022-02-12 13:02:31 -05:00
|
|
|
import {Calendar} from './Calendar';
|
2023-07-15 15:37:25 -04:00
|
|
|
import {FrenchMonth, frEndJD, frStartJD, frSupportedYear, jdnFrench} from '@common/french';
|
|
|
|
import {dateJDN} from '@common/gregorian';
|
2022-02-27 03:13:42 -05:00
|
|
|
import {TimeOfDay} from './TimeOfDay';
|
2023-07-15 15:37:25 -04:00
|
|
|
import {GregorianJumper} from '@common/dateJump';
|
2022-02-12 09:44:58 -05:00
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
class App extends React.Component<{}, AppState> {
|
|
|
|
state: AppState;
|
|
|
|
|
|
|
|
constructor(props: {}) {
|
|
|
|
super(props);
|
2023-07-15 15:37:25 -04:00
|
|
|
const todayJDN = dateJDN(new Date());
|
2022-02-12 16:42:29 -05:00
|
|
|
const {year, month} = jdnFrench(todayJDN);
|
2022-02-12 16:25:44 -05:00
|
|
|
|
|
|
|
this.state = {
|
2022-02-12 16:42:29 -05:00
|
|
|
...(parseURL() || {year, month}),
|
|
|
|
todayJDN,
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(state: any, callback?: () => void) {
|
|
|
|
super.setState(state, () => {
|
|
|
|
this.updateURL();
|
2023-07-15 15:37:25 -04:00
|
|
|
callback?.();
|
2022-02-12 16:25:44 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2023-07-15 15:37:25 -04:00
|
|
|
goToJDN = (jdn: number) => {
|
|
|
|
const {year, month} = jdnFrench(Math.min(Math.max(frStartJD, jdn), frEndJD));
|
|
|
|
this.setState({year, month});
|
|
|
|
};
|
|
|
|
|
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>
|
2023-07-15 15:37:25 -04:00
|
|
|
<GregorianJumper minJDN={frStartJD} maxJDN={frEndJD} todayJDN={this.state.todayJDN}
|
|
|
|
onJump={this.goToJDN}/>
|
2022-02-14 01:48:09 -05:00
|
|
|
</div>
|
|
|
|
</>;
|
2022-02-12 16:25:44 -05:00
|
|
|
}
|
2022-02-12 09:44:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
export default App;
|