/* This file is part of GNU Taler (C) 2021 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * * @author Sebastian Javier Marchano (sebasjm) */ import { h, Component } from "preact"; interface Props { closeFunction?: () => void; dateReceiver?: (d: Date) => void; opened?: boolean; } interface State { displayedMonth: number; displayedYear: number; selectYearMode: boolean; currentDate: Date; } const now = new Date() const monthArrShortFull = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] const monthArrShort = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] const dayArr = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] const yearArr: number[] = [] // inspired by https://codepen.io/m4r1vs/pen/MOOxyE export class DatePicker extends Component { closeDatePicker() { this.props.closeFunction && this.props.closeFunction(); // Function gets passed by parent } /** * Gets fired when a day gets clicked. * @param {object} e The event thrown by the element clicked */ dayClicked(e: any) { const element = e.target; // the actual element clicked if (element.innerHTML === '') return false; // don't continue if empty // get date from clicked element (gets attached when rendered) const date = new Date(element.getAttribute('data-value')); // update the state this.setState({ currentDate: date }); this.passDateToParent(date) } /** * returns days in month as array * @param {number} month the month to display * @param {number} year the year to display */ getDaysByMonth(month: number, year: number) { const calendar = []; const date = new Date(year, month, 1); // month to display const firstDay = new Date(year, month, 1).getDay(); // first weekday of month const lastDate = new Date(year, month + 1, 0).getDate(); // last date of month let day: number | null = 0; // the calendar is 7*6 fields big, so 42 loops for (let i = 0; i < 42; i++) { if (i >= firstDay && day !== null) day = day + 1; if (day !== null && day > lastDate) day = null; // append the calendar Array calendar.push({ day: (day === 0 || day === null) ? null : day, // null or number date: (day === 0 || day === null) ? null : new Date(year, month, day), // null or Date() today: (day === now.getDate() && month === now.getMonth() && year === now.getFullYear()) // boolean }); } return calendar; } /** * Display previous month by updating state */ displayPrevMonth() { if (this.state.displayedMonth <= 0) { this.setState({ displayedMonth: 11, displayedYear: this.state.displayedYear - 1 }); } else { this.setState({ displayedMonth: this.state.displayedMonth - 1 }); } } /** * Display next month by updating state */ displayNextMonth() { if (this.state.displayedMonth >= 11) { this.setState({ displayedMonth: 0, displayedYear: this.state.displayedYear + 1 }); } else { this.setState({ displayedMonth: this.state.displayedMonth + 1 }); } } /** * Display the selected month (gets fired when clicking on the date string) */ displaySelectedMonth() { if (this.state.selectYearMode) { this.toggleYearSelector(); } else { if (!this.state.currentDate) return false; this.setState({ displayedMonth: this.state.currentDate.getMonth(), displayedYear: this.state.currentDate.getFullYear() }); } } toggleYearSelector() { this.setState({ selectYearMode: !this.state.selectYearMode }); } changeDisplayedYear(e: any) { const element = e.target; this.toggleYearSelector(); this.setState({ displayedYear: parseInt(element.innerHTML, 10), displayedMonth: 0 }); } /** * Pass the selected date to parent when 'OK' is clicked */ passSavedDateDateToParent() { this.passDateToParent(this.state.currentDate) } passDateToParent(date: Date) { if (typeof this.props.dateReceiver === 'function') this.props.dateReceiver(date); this.closeDatePicker(); } componentDidUpdate() { if (this.state.selectYearMode) { document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it } } constructor() { super(); this.closeDatePicker = this.closeDatePicker.bind(this); this.dayClicked = this.dayClicked.bind(this); this.displayNextMonth = this.displayNextMonth.bind(this); this.displayPrevMonth = this.displayPrevMonth.bind(this); this.getDaysByMonth = this.getDaysByMonth.bind(this); this.changeDisplayedYear = this.changeDisplayedYear.bind(this); this.passDateToParent = this.passDateToParent.bind(this); this.toggleYearSelector = this.toggleYearSelector.bind(this); this.displaySelectedMonth = this.displaySelectedMonth.bind(this); this.state = { currentDate: now, displayedMonth: now.getMonth(), displayedYear: now.getFullYear(), selectYearMode: false } } render() { const { currentDate, displayedMonth, displayedYear, selectYearMode } = this.state; return (

{currentDate.getFullYear()}

{dayArr[currentDate.getDay()]}, {monthArrShort[currentDate.getMonth()]} {currentDate.getDate()}

{!selectYearMode && }
{!selectYearMode &&
{['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day,i) => {day})}
{/* Loop through the calendar object returned by getDaysByMonth(). */} {this.getDaysByMonth(this.state.displayedMonth, this.state.displayedYear) .map( day => { let selected = false; if (currentDate && day.date) selected = (currentDate.toLocaleDateString() === day.date.toLocaleDateString()); return ( {day.day} ) } ) }
} {selectYearMode &&
{yearArr.map(year => ( {year} ))}
}
) } } for (let i = 2010; i <= now.getFullYear() + 10; i++) { yearArr.push(i); }