Last active
June 26, 2023 08:59
-
-
Save stevensacks/79c60d0f8b1f8bc06b475438f59d687e to your computer and use it in GitHub Desktop.
Revisions
-
stevensacks revised this gist
Jan 6, 2019 . 3 changed files with 15 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,15 +2,17 @@ import {addDays, format, isAfter, isBefore, startOfDay} from 'date-fns'; import React, {Component, Fragment} from 'react'; import classes from 'classnames'; import DatePickerDialog from './DatePickerDialog'; import Dialog from 'components/Dialog'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {getDisabled} from '../../../utils/component'; import PropTypes from 'prop-types'; import './index.css'; export default class DatePicker extends Component { static propTypes = { className: PropTypes.string, dateFormat: PropTypes.string, disabled: PropTypes.bool, endDate: PropTypes.instanceOf(Date), isRange: PropTypes.bool, maxDate: PropTypes.instanceOf(Date), @@ -78,17 +80,22 @@ export default class DatePicker extends Component { ); render() { const {className, dateFormat, disabled, isRange} = this.props; const {startDate, endDate} = this.state; const formattedStartDate = format(startDate, dateFormat); const formattedEndDate = isRange ? format(endDate, dateFormat) : ''; const click = !disabled ? this.onClick : undefined; return ( <div className={classes('date-picker', { [className]: !!className, })} > <a className="button date-picker-button" onClick={click} {...getDisabled(disabled)} > <span className="icon"> <FontAwesomeIcon icon={['fas', 'calendar-alt']} /> </span> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -56,8 +56,8 @@ export const DatePickerCalendar = ({ const onClick = !isValidDate(theDate, minDate, maxDate) ? null : isThisMonth ? onClickDate : onClickJump; return { date: theDate, isToday: isToday(theDate), This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,9 @@ import React, {Fragment} from 'react'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {format} from 'date-fns'; import PropTypes from 'prop-types'; /* eslint-disable jsx-a11y/anchor-is-valid */ export const DatePickerNav = ({ visibleDate, -
stevensacks revised this gist
Aug 19, 2018 . 1 changed file with 5 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -32,13 +32,14 @@ export default class DatePicker extends Component { !props.minDate || isAfter(props.startDate, props.minDate) ? props.startDate : props.minDate; const endDate = props.isRange ? !props.maxDate || isBefore(props.endDate, props.maxDate) ? props.endDate : props.maxDate : undefined; this.state = { startDate: startOfDay(startDate), endDate: endDate ? startOfDay(endDate) : undefined, }; } -
stevensacks revised this gist
Aug 19, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ # DatePicker The DatePicker component allows you to pick a single date, or date range (from -> to). This component is built on top of my [Dialog as a promise component](https://gist.github.com/stevensacks/b2fb14ef89e33aecdb0ca0a7a8b4f11a) and uses [Bulma](https://bulma.io). It uses the SASS from [Bulma Calendar](https://wikiki.github.io/components/calendar/), converted to SCSS. -
stevensacks revised this gist
Aug 19, 2018 . 3 changed files with 11 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -122,9 +122,9 @@ export default class DatePickerDialog extends Component { } else if (isAfter(date, endDate)) { // extend the end date newDates.endDate = date; } else if (isBefore(date, endDate)) { // truncate end date newDates.endDate = date; } this.setState({...newDates, visibleDate: date}); this.props.onUpdate(newDates); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,9 @@ The DatePicker component allows you to pick a single date, or date range (from - This component is built on top of my [Dialog as a promise component](https://gist.github.com/stevensacks/b2fb14ef89e33aecdb0ca0a7a8b4f11a). It uses the SASS from [Bulma Calendar](https://wikiki.github.io/components/calendar/), converted to SCSS. The Javascript has been rewritten from scratch for use in React and uses the [date-fns](https://date-fns.org/docs/) library. **Required Props** * `onChange` - When you select a date or a date range, this returns the selection (see below). This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ @import 'bulma/sass/utilities/functions'; @import 'bulma/sass/utilities/derived-variables'; @@ -30,6 +29,7 @@ .modal-card { display: block; width: auto; height: 403px; } .modal-card-body { @@ -42,6 +42,10 @@ display: flex; padding: 0.5em; } .modal-card { height: 487px; } } } } -
stevensacks revised this gist
Aug 19, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,7 @@ The DatePicker component allows you to pick a single date, or date range (from - This component is built on top of my [Dialog as a promise component](https://gist.github.com/stevensacks/b2fb14ef89e33aecdb0ca0a7a8b4f11a). It uses the SASS from [Bulma Calendar](https://wikiki.github.io/components/calendar/), but the source Javascript has been rewritten from scratch for use in React. **Required Props** * `onChange` - When you select a date or a date range, this returns the selection (see below). -
stevensacks revised this gist
Aug 19, 2018 . 2 changed files with 38 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -16,7 +16,7 @@ export default class DatePicker extends Component { maxDate: PropTypes.instanceOf(Date), minDate: PropTypes.instanceOf(Date), name: PropTypes.string, onChange: PropTypes.func.isRequired, startDate: PropTypes.instanceOf(Date), }; @@ -42,8 +42,6 @@ export default class DatePicker extends Component { }; } onClick = () => Dialog({ title: '', @@ -64,14 +62,14 @@ export default class DatePicker extends Component { endDate: this.state.endDate, }, }, }).then(this.onChange); onChange = event => event && this.setState( {startDate: event.startDate, endDate: event.endDate}, () => this.props.onChange({ name: this.props.name, startDate: this.state.startDate, endDate: this.state.endDate, This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,34 @@ # DatePicker The DatePicker component allows you to pick a single date, or date range (from -> to). This component is built on top of my [Dialog as a promise component](https://gist.github.com/stevensacks/b2fb14ef89e33aecdb0ca0a7a8b4f11a). It uses the SASS from [Bulma Calendar](https://wikiki.github.io/components/calendar/), rewritten entirely for use in React. **Required Props** * `onChange` - When you select a date or a date range, this returns the selection (see below). **Optional Props** * `name` - The data name value which will be included in the onChange callback (see below) * `startDate` - The date that you want to be pre-selected in a single date or range. The default is today. * `isRange` - If you want the DatePicker to act as a range picker, include this boolean prop (see below). * `endDate` - If the component is a range picker, this is the end date you want to be pre-selected. The default is tomorrow. * `minDate` - Optional minimum threshold for only allowing dates from this date or after. * `maxDate` - Optional maximum threshold for only allowing dates from this date or before. * `dateFormat` - What is displayed on the button, as per the [date-fns format](https://date-fns.org/v1.29.0/docs/format) documentation. The default is `YYYY-MM-DD`. * `className` - A custom className for styling. ```javascript const onChange = ({name, startDate, endDate}) => { console.log({name, startDate, endDate}); }; <DatePicker name="availabilityWindow" startDate={new Date(2018, 2, 5)} // March 5th, 2018 endDate={new Date(2018, 3, 12)} // April 12th, 2018 minDate={new Date(2018, 1, 1)} // Feb 1st, 2018 maxDate={new Date(2018, 4, 31)} // May 31st, 2018 onChange={onChange} isRange /> -
stevensacks revised this gist
Aug 18, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -22,7 +22,7 @@ export default class DatePicker extends Component { static defaultProps = { dateFormat: 'YYYY-MM-DD', startDate: new Date(), endDate: addDays(new Date(), 1), }; -
stevensacks revised this gist
Aug 18, 2018 . 1 changed file with 358 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,358 @@ @import 'css/vars.scss'; @import 'bulma/sass/utilities/functions'; @import 'bulma/sass/utilities/derived-variables'; // scss-lint:disable NameFormat .date-picker { position: relative; .button { &.date-picker-button { position: relative; .icon { position: relative; top: -1px; } padding-top: 0.375em; padding-bottom: calc(0.375em - 2px); } } &-dialog { header, footer { display: none; } .modal-card { display: block; width: auto; } .modal-card-body { flex: none; padding: 0; } &-range { footer { display: flex; padding: 0.5em; } } } } $calendar-border: none !default; $calendar-border-radius: $radius-small !default; $calendar-header-background-color: $primary !default; $calendar-days-background-color: transparent !default; $calendar-header-days-color: $grey-light !default; $calendar-date-color: $text !default; $calendar-date-hover-background-color: $white-ter !default; $calendar-today-background: transparent !default; $calendar-today-border-color: $primary !default; $calendar-today-color: $primary !default; $calendar-range-background-color: lighten($primary, 50%) !default; $calendar-body-padding: 0 1em 1em 1em !default; $calendar-header-padding: 1em 1em 0 1em !default; $calendar-header-nav-padding: 0.5em !default; $calendar-date-padding: 0.4rem 0 !default; .calendar { position: relative; display: block; min-width: 20rem; max-width: 20rem; border: $calendar-border; border-radius: $calendar-border-radius; background: $white; text-align: center; &.is-active { display: initial; } .calendar-nav { display: flex; align-items: center; justify-content: space-between; padding: $calendar-header-nav-padding; border-top-left-radius: $calendar-border-radius; border-top-right-radius: $calendar-border-radius; background: $calendar-header-background-color; color: $white; font-size: $size-5; .calendar-nav-month, .calendar-nav-day, .calendar-nav-year { display: flex; justify-content: space-between; align-items: center; width: 100%; } .calendar-month, .calendar-day, .calendar-year { flex: 1; } .calendar-month { font-size: $size-4; user-select: none; } .calendar-day { font-size: $size-2; } .calendar-nav-prev-month, .calendar-nav-next-month, .calendar-nav-prev-year, .calendar-nav-next-year { flex-basis: auto; flex-grow: 0; flex-shrink: 0; color: $white; text-decoration: none; &:hover { background-color: transparent; svg { stroke-width: 1em; } } svg { stroke: currentColor; width: 11.25px; height: 18px; } } &-range { justify-content: center; margin-top: -5px; padding: 0 0.5em; .icon { margin: 0 5px; } } } .calendar-header, .calendar-body { display: flex; flex-wrap: wrap; justify-content: center; } .calendar-header .calendar-date, .calendar-body .calendar-date { flex: 0 0 14.28%; max-width: 14.28%; } .calendar-header { padding: $calendar-header-padding; background: $calendar-days-background-color; color: findColorInvert($calendar-days-background-color); font-size: $size-7; user-select: none; .calendar-date { color: $calendar-header-days-color; } } .calendar-body { padding: $calendar-body-padding; color: $grey; } .calendar-date { padding: $calendar-date-padding; border: 0; user-select: none; .date-item { position: relative; padding: 0.3rem; width: 2.2rem; height: 2.2rem; outline: none; border: 0.1rem solid transparent; border-radius: 100%; background: transparent; color: $calendar-date-color; vertical-align: middle; text-align: center; text-decoration: none; white-space: nowrap; line-height: 1.4rem; cursor: pointer; transition: all 0.2s ease; appearance: none; &.is-today { border-color: $calendar-today-border-color; background: $calendar-today-background; color: $calendar-today-color; } &:focus { border-color: $calendar-date-hover-background-color; background: $calendar-date-hover-background-color; color: findColorInvert($calendar-date-hover-background-color); text-decoration: none; } &:hover { border-color: $calendar-date-hover-background-color; background: $calendar-date-hover-background-color; color: findColorInvert($calendar-date-hover-background-color); text-decoration: none; } &.is-active { border-color: $primary; background: $primary; color: findColorInvert($primary); } } &.is-disabled { .date-item, .calendar-event { opacity: 0.1; cursor: default; pointer-events: none; } } &.is-outside-month { &:not(.calendar-range) { .date-item { opacity: 0.4; } } &.is-disabled { .date-item { opacity: 0.1; } } } } .calendar-range { position: relative; &::before { position: absolute; top: 50%; right: 0; left: 0; height: 2.2rem; background: $calendar-range-background-color; content: ''; transform: translateY(-50%); } &.calendar-range-start::before { left: 50%; } &.calendar-range-end::before { right: 50%; } .date-item { color: $primary; } } &.is-large { max-width: 100%; .calendar-body { .calendar-date { display: flex; flex-direction: column; padding: 0; height: 11rem; border-right: $calendar-border; border-bottom: $calendar-border; &:nth-child(7n) { border-right: 0; } &:nth-last-child(-n + 7) { border-bottom: 0; } } } .date-item { margin-top: 0.5rem; margin-right: 0.5rem; align-self: flex-end; height: 2.2rem; } .calendar-range { &::before { top: 1.9rem; } &.calendar-range-start::before { left: auto; width: 1.9rem; } &.calendar-range-end::before { right: 1.9rem; } } .calendar-events { overflow-y: auto; padding: 0.5rem; flex-grow: 1; line-height: 1; } .calendar-event { display: block; overflow: hidden; margin: 0.2rem auto; padding: 0.3rem 0.4rem; border-radius: $radius-small; background-color: $grey; color: $white; vertical-align: baseline; text-align: left; text-overflow: ellipsis; white-space: nowrap; font-size: 1rem; @each $name, $pair in $colors { $color: nth($pair, 1); $color-invert: nth($pair, 2); &.is-#{$name} { background-color: $color; color: $color-invert; } } } } } -
stevensacks created this gist
Aug 18, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,111 @@ import {addDays, format, isAfter, isBefore, startOfDay} from 'date-fns'; import React, {Component, Fragment} from 'react'; import classes from 'classnames'; import DatePickerDialog from './DatePickerDialog'; import {Dialog} from 'core'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import PropTypes from 'prop-types'; import './index.css'; export default class DatePicker extends Component { static propTypes = { className: PropTypes.string, dateFormat: PropTypes.string, endDate: PropTypes.instanceOf(Date), isRange: PropTypes.bool, maxDate: PropTypes.instanceOf(Date), minDate: PropTypes.instanceOf(Date), name: PropTypes.string, onSelect: PropTypes.func.isRequired, startDate: PropTypes.instanceOf(Date), }; static defaultProps = { dateFormat: 'YYYY-MM-DD', startDate: startOfDay(new Date()), endDate: addDays(new Date(), 1), }; constructor(props) { super(props); const startDate = !props.minDate || isAfter(props.startDate, props.minDate) ? props.startDate : props.minDate; const endDate = !props.maxDate || isBefore(props.endDate, props.maxDate) ? props.endDate : props.maxDate; this.state = { startDate: startOfDay(startDate), endDate: props.isRange ? startOfDay(endDate) : undefined, }; } // This is my custom Dialog opener // it opens a Bulma modal with a modal-card onClick = () => Dialog({ title: '', styles: { dialog: classes('date-picker-dialog', { 'date-picker-dialog-range': this.props.isRange, }), }, cancel: this.props.isRange ? 'Cancel' : undefined, submit: this.props.isRange ? 'Save' : undefined, custom: { View: DatePickerDialog, props: { maxDate: this.props.maxDate, minDate: this.props.minDate, isRange: this.props.isRange, startDate: this.state.startDate, endDate: this.state.endDate, }, }, }).then(this.onSelect); onSelect = event => event && this.setState( {startDate: event.startDate, endDate: event.endDate}, () => this.props.onSelect({ name: this.props.name, startDate: this.state.startDate, endDate: this.state.endDate, }) ); render() { const {className, dateFormat, isRange} = this.props; const {startDate, endDate} = this.state; const formattedStartDate = format(startDate, dateFormat); const formattedEndDate = isRange ? format(endDate, dateFormat) : ''; return ( <div className={classes('date-picker', { [className]: !!className, })} > <a className="button date-picker-button" onClick={this.onClick}> <span className="icon"> <FontAwesomeIcon icon={['fas', 'calendar-alt']} /> </span> <span>{formattedStartDate}</span> {isRange && ( <Fragment> <span className="icon"> <FontAwesomeIcon icon={['fas', 'long-arrow-alt-right']} /> </span> <span>{formattedEndDate}</span> </Fragment> )} </a> </div> ); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,102 @@ import { addDays, differenceInDays, endOfMonth, endOfWeek, format, isAfter, isBefore, isEqual, isSameMonth, isToday, isWithinRange, startOfMonth, startOfWeek, } from 'date-fns'; import DatePickerDate from './DatePickerDate'; import PropTypes from 'prop-types'; import React from 'react'; const isValidDate = (date, minDate, maxDate) => { if (!minDate && !maxDate) return true; if (minDate && maxDate) return isWithinRange(date, minDate, maxDate); if (maxDate) return isBefore(date, maxDate) || isEqual(date, maxDate); return isAfter(date, minDate) || isEqual(date, minDate); }; export const DatePickerCalendar = ({ endDate, isRange, maxDate, minDate, onClickDate, onClickJump, startDate, visibleDate, }) => { // the 7 days of the week (Sun-Sat) const labels = new Array(7) .fill(startOfWeek(visibleDate)) .map((d, i) => format(addDays(d, i), 'ddd')); // first day of current month view const start = startOfWeek(startOfMonth(visibleDate)); // last day of current month view const end = endOfWeek(endOfMonth(visibleDate)); // get all days and whether they are within the current month and range const days = new Array(differenceInDays(end, start) + 1) .fill(start) .map((s, i) => { const theDate = addDays(s, i); const isThisMonth = isSameMonth(visibleDate, theDate); const isInRange = isRange && isWithinRange(theDate, startDate, endDate); // if not in range, no click action // if in this month, select the date // if out of this month, jump to the date const onClick = !isValidDate(theDate, minDate, maxDate) ? null : isThisMonth ? onClickDate : onClickJump; return { date: theDate, isToday: isToday(theDate), isStartDate: isEqual(startDate, theDate), isEndDate: isEqual(endDate, theDate), isThisMonth, isInRange, onClick, }; }); return ( <div className="calendar-container"> <div className="calendar-header"> {labels.map(day => ( <div key={day} className="calendar-date"> {day} </div> ))} </div> <div className="calendar-body"> {days.map(theDate => ( <DatePickerDate key={theDate.date.toString()} {...theDate} /> ))} </div> </div> ); }; DatePickerCalendar.propTypes = { endDate: PropTypes.instanceOf(Date), isRange: PropTypes.bool, maxDate: PropTypes.instanceOf(Date), minDate: PropTypes.instanceOf(Date), onClickDate: PropTypes.func.isRequired, onClickJump: PropTypes.func.isRequired, startDate: PropTypes.instanceOf(Date), visibleDate: PropTypes.instanceOf(Date), }; export default DatePickerCalendar; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,45 @@ import classes from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; export const DatePickerDate = ({ date, isEndDate, isInRange, isStartDate, isThisMonth, isToday, onClick, }) => ( <div className={classes('calendar-date', { 'is-outside-month': !isThisMonth, 'is-disabled': !onClick, 'calendar-range': isInRange, 'calendar-range-start': isInRange && isStartDate, 'calendar-range-end': isInRange && isEndDate, })} > <button className={classes('date-item', { 'is-today': isToday, 'is-active': isStartDate || isEndDate, })} onClick={onClick ? () => onClick(date) : null} > {date.getDate()} </button> </div> ); DatePickerDate.propTypes = { date: PropTypes.instanceOf(Date).isRequired, isEndDate: PropTypes.bool, isInRange: PropTypes.bool, isStartDate: PropTypes.bool, isThisMonth: PropTypes.bool, isToday: PropTypes.bool, onClick: PropTypes.func, }; export default DatePickerDate; This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,160 @@ import { addDays, addMonths, getDaysInMonth, isAfter, isBefore, isEqual, lastDayOfMonth, setDate, subDays, subMonths, } from 'date-fns'; import React, {Component} from 'react'; import DatePickerCalendar from './DatePickerCalendar'; import DatePickerNav from './DatePickerNav'; import PropTypes from 'prop-types'; export default class DatePickerDialog extends Component { static propTypes = { endDate: PropTypes.instanceOf(Date), isRange: PropTypes.bool, maxDate: PropTypes.instanceOf(Date), minDate: PropTypes.instanceOf(Date), onUpdate: PropTypes.func, resolve: PropTypes.func, startDate: PropTypes.instanceOf(Date), }; constructor(props) { super(props); this.state = { startDate: props.startDate, endDate: props.endDate, visibleDate: props.startDate, }; } onNext = () => this.setState(prevState => { const nextMonth = addMonths(prevState.visibleDate, 1); const day = Math.min( getDaysInMonth(nextMonth), prevState.visibleDate.getDate() ); if ( !this.props.maxDate || isBefore(nextMonth, this.props.maxDate) ) { const visibleDate = setDate(nextMonth, day); return { visibleDate, }; } return { visibleDate: this.props.maxDate, }; }); onPrev = () => this.setState(prevState => { const prevMonth = lastDayOfMonth( subMonths( new Date( prevState.visibleDate.getFullYear(), prevState.visibleDate.getMonth() ), 1 ) ); const day = Math.min( getDaysInMonth(prevMonth), prevState.visibleDate.getDate() ); if (!this.props.minDate || isAfter(prevMonth, this.props.minDate)) { const visibleDate = setDate(prevMonth, day); return { visibleDate, }; } return { visibleDate: this.props.minDate, }; }); onClickJump = date => { if (!this.props.isRange) { this.props.resolve({startDate: date}); } else { this.setStartAndEnd(date); } }; onClickDate = date => { if (!this.props.isRange) { this.props.resolve({startDate: date}); } else { this.setStartAndEnd(date); } }; onClickNav = date => { this.setState({visibleDate: date}); }; setStartAndEnd = date => { const {startDate, endDate} = this.state; const newDates = { startDate, endDate, }; if (isEqual(date, startDate)) { // reset start and end dates anchored to start newDates.startDate = date; newDates.endDate = addDays(date, 1); } else if (isEqual(date, endDate)) { // reset start and end dates anchored to end newDates.startDate = subDays(date, 1); newDates.endDate = date; } else if (isBefore(date, startDate)) { // extend the start date newDates.startDate = date; } else if (isAfter(date, endDate)) { // extend the end date newDates.endDate = date; } else if (isAfter(date, startDate)) { // truncate start date newDates.startDate = date; } this.setState({...newDates, visibleDate: date}); this.props.onUpdate(newDates); }; render() { const {isRange} = this.props; const {startDate, endDate, visibleDate} = this.state; return ( <div className="calendar"> <DatePickerNav visibleDate={visibleDate} startDate={startDate} endDate={endDate} isRange={isRange} onNext={this.onNext} onPrev={this.onPrev} onJump={this.onClickNav} /> <DatePickerCalendar visibleDate={visibleDate} startDate={startDate} endDate={endDate} isRange={isRange} minDate={this.props.minDate} maxDate={this.props.maxDate} onClickDate={this.onClickDate} onClickJump={this.onClickJump} /> </div> ); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,67 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {format} from 'date-fns'; import PropTypes from 'prop-types'; import React, {Fragment} from 'react'; export const DatePickerNav = ({ visibleDate, startDate, endDate, isRange, onJump, onNext, onPrev, }) => ( <Fragment> <div className="calendar-nav"> <div className="calendar-nav-prev-month" onClick={onPrev}> <button className="button is-primary"> <span className="icon"> <FontAwesomeIcon icon={['fas', 'chevron-left']} /> </span> </button> </div> <div className="calendar-month"> {format(visibleDate, 'MMMM YYYY')} </div> <div className="calendar-nav-next-month" onClick={onNext}> <button className="button is-primary"> <span className="icon"> <FontAwesomeIcon icon={['fas', 'chevron-right']} /> </span> </button> </div> </div> {isRange && ( <div className="calendar-nav calendar-nav-range"> <a className="button is-primary" onClick={() => onJump(startDate)} > {format(startDate, 'YYYY-MM-DD')} </a> <span className="icon"> <FontAwesomeIcon icon={['fas', 'long-arrow-alt-right']} /> </span> <a className="button is-primary" onClick={() => onJump(endDate)} > {format(endDate, 'YYYY-MM-DD')} </a> </div> )} </Fragment> ); DatePickerNav.propTypes = { endDate: PropTypes.instanceOf(Date), isRange: PropTypes.bool, onJump: PropTypes.func, onNext: PropTypes.func.isRequired, onPrev: PropTypes.func.isRequired, startDate: PropTypes.instanceOf(Date), visibleDate: PropTypes.instanceOf(Date), }; export default DatePickerNav;