Created
June 16, 2018 00:06
-
-
Save ehahn9/704bf086c058c74e3a5a43067d053089 to your computer and use it in GitHub Desktop.
This file contains 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 characters
import React, { Component } from 'react'; | |
import PropTypes from 'prop-types'; | |
import { accessor } from 'react-big-calendar/lib/utils/accessors'; | |
import { accessor as accessorPropType } from 'react-big-calendar/lib/utils/propTypes'; | |
import { noop } from 'lodash'; | |
import { zonedToLocal, localToZoned } from '/client/utils/timezones'; | |
import { hasTime, shiftDate, shiftHour } from '/client/utils/date'; | |
/** | |
* withTimeZone - HOC to add time zone support to react-big-calendar | |
* | |
* import Calendar from 'react-big-calendar'; | |
* import withTimeZone from './withTimeZone'; | |
* const ZonedCalendar = withTimeZone(Calendar) | |
* | |
* render() { | |
* return <ZonedCalendar zone="Pacific/Honolulu" ... /> | |
* } | |
*/ | |
export default function withTimeZone(Calendar) { | |
return class TimeZoneCalendar extends Component { | |
static propTypes = { | |
events: PropTypes.array, | |
date: PropTypes.instanceOf(Date).isRequired, | |
zone: PropTypes.string.isRequired, | |
startAccessor: accessorPropType, | |
endAccessor: accessorPropType, | |
allDayAccessor: accessorPropType, | |
onEventDrop: PropTypes.func, | |
onEventResize: PropTypes.func, | |
onSelectSlot: PropTypes.func | |
}; | |
static defaultProps = { | |
startAccessor: 'start', | |
endAccessor: 'end', | |
allDayAccessor: 'allDay', | |
onEventDrop: noop, | |
onEventResize: noop, | |
onSelectSlot: noop | |
}; | |
startAccessor = (event) => this.timeAccessor(event, 'startAccessor'); | |
endAccessor = (event) => this.timeAccessor(event, 'endAccessor'); | |
timeAccessor = (event, accessorName) => { | |
const value = accessor(event, this.props[accessorName]); | |
const allDay = accessor(event, this.props.allDayAccessor); | |
const zone = allDay ? 'UTC' : this.props.zone; | |
// see discussion in timeszones.js about zonedToLocal. allDay events are in UTC | |
return zonedToLocal(value, zone); | |
} | |
handleSelectSlot = ({ start, end, slots }) => { | |
// FIXME: this is basically the same logic as toZonedUpdate! DRY it out | |
const allDay = !hasTime(start) && !hasTime(end); | |
const zone = allDay ? 'UTC' : this.props.zone; | |
// r-b-c has no time zones - whatever time it says is really in the | |
// local time zone and we need to convert it to the same time in | |
// the target zones (except allDay events are always in UTC). | |
// | |
// also, when selecting allDay events, r-b-c gives the end time | |
// as an INCLUSIVE date (e.g. a one day allDay will start and end | |
// on the same date), but events have the end as an EXCLUSIVE time | |
// (e.g. one day events have end on the NEXT day) - so fix that here. | |
start = localToZoned(start, zone); | |
if (allDay) end = shiftDate(end, 1); // FIXME: does this really belong in withTimeZone - fix up with new r-b-c allDay, etc. | |
end = localToZoned(end, zone); | |
slots = slots.map(date => localToZoned(date, zone)); // TODO: I don't understand the slots arg to onSelectSlot | |
this.props.onSelectSlot({ start, end, allDay, slots }); | |
} | |
handleEventDrop = ({ event, start, end }) => | |
this.props.onEventDrop(this.toZonedUpdate({ event, start, end })); | |
handleResize = (resizeType, { event, start, end }) => | |
this.props.onEventResize(resizeType, this.toZonedUpdate({ event, start, end })); | |
toZonedUpdate = ({ event, start, end }) => { | |
// since we have nothing better, allDay is determined by the start date | |
// only because RBC computes the end date as start+duration | |
const allDay = !hasTime(start); | |
const zone = allDay ? 'UTC' : this.props.zone; | |
// if we're dropping an allDay event onto the grid (non-allDay), | |
// make a 1 hour event | |
if (event.allDay && !allDay) { | |
end = shiftHour(start, 1); | |
} | |
// TODO: there's a bug in rbc where dragging from a non-allDay | |
// to an allDay just returns the drag as if it was the same | |
// time on the new day, so alas, this case won't work: | |
// if (allDay && !event.allDay) { | |
// end = addOne(start, 'day'); | |
// } | |
// TODO: fix this in r-b-c | |
// just like selection, if we're creating an allDay event, | |
// create it in UTC zone. | |
start = localToZoned(start, zone); | |
end = localToZoned(end, zone); | |
return { event, start, end, allDay }; | |
} | |
render() { | |
const { date, zone, ...props } = this.props; | |
const bigCalendarProps = { | |
...props, | |
date: zonedToLocal(date, zone), | |
getNow: () => zonedToLocal(new Date(), zone), | |
startAccessor: this.startAccessor, | |
endAccessor: this.endAccessor, | |
onSelectSlot: this.handleSelectSlot, | |
onSelectEvent: this.handleSelectEvent, | |
onEventDrop: this.handleEventDrop, | |
onEventResize: this.handleResize | |
}; | |
return <Calendar {...bigCalendarProps} />; | |
} | |
}; | |
} |
Sure - this is really ancient stuff and alas, I'm not currently working on it any longer (I'd suggest using date-fns-tz instead)... but here you go!
Thank you!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would you be able to release the code for these imports as well?