diff --git a/src/agenda/index.js b/src/agenda/index.js index 8451908dc7..dc154ada5c 100644 --- a/src/agenda/index.js +++ b/src/agenda/index.js @@ -1,20 +1,14 @@ -import React, {Component} from 'react'; -import { - Text, - View, - Dimensions, - Animated, - ViewPropTypes, -} from 'react-native'; -import PropTypes from 'prop-types'; -import XDate from 'xdate'; - -import {parseDate, xdateToData} from '../interface'; -import dateutils from '../dateutils'; -import CalendarList from '../calendar-list'; -import ReservationsList from './reservation-list'; -import styleConstructor from './style'; -import { VelocityTracker } from '../input'; +import React, { Component } from "react"; +import { Text, View, Dimensions, Animated, ViewPropTypes } from "react-native"; +import PropTypes from "prop-types"; +import XDate from "xdate"; + +import { parseDate, xdateToData } from "../interface"; +import dateutils from "../dateutils"; +import CalendarList from "../calendar-list"; +import ReservationsList from "./reservation-list"; +import styleConstructor from "./style"; +import { VelocityTracker } from "../input"; const HEADER_HEIGHT = 104; const KNOB_HEIGHT = 24; @@ -28,7 +22,7 @@ export default class AgendaView extends Component { theme: PropTypes.object, // agenda container style - style: viewPropTypes.style, + style: PropTypes.style, // the list of items that have to be displayed in agenda. If you want to render item as empty date // the value of date key has to be an empty array []. If there exists no value for date key it is @@ -94,11 +88,11 @@ export default class AgendaView extends Component { constructor(props) { super(props); this.styles = styleConstructor(props.theme); - const windowSize = Dimensions.get('window'); + const windowSize = Dimensions.get("window"); this.viewHeight = windowSize.height; this.viewWidth = windowSize.width; this.scrollTimeout = undefined; - this.headerState = 'idle'; + this.headerState = "idle"; this.state = { scrollY: new Animated.Value(0), calendarIsReady: false, @@ -116,11 +110,11 @@ export default class AgendaView extends Component { this.onSnapAfterDrag = this.onSnapAfterDrag.bind(this); this.generateMarkings = this.generateMarkings.bind(this); this.knobTracker = new VelocityTracker(); - this.state.scrollY.addListener(({value}) => this.knobTracker.add(value)); + this.state.scrollY.addListener(({ value }) => this.knobTracker.add(value)); } calendarOffset() { - return 90 - (this.viewHeight / 2); + return 90 - this.viewHeight / 2; } initialScrollPadPosition() { @@ -128,7 +122,7 @@ export default class AgendaView extends Component { } setScrollPadPosition(y, animated) { - this.scrollPad._component.scrollTo({x: 0, y, animated}); + this.scrollPad._component.scrollTo({ x: 0, y, animated }); } onScrollPadLayout() { @@ -137,7 +131,7 @@ export default class AgendaView extends Component { // scroll position actually changes (it would stay at 0, when scrolled to the top). this.setScrollPadPosition(this.initialScrollPadPosition(), false); // delay rendering calendar in full height because otherwise it still flickers sometimes - setTimeout(() => this.setState({calendarIsReady: true}), 0); + setTimeout(() => this.setState({ calendarIsReady: true }), 0); } onLayout(event) { @@ -147,26 +141,26 @@ export default class AgendaView extends Component { } onTouchStart() { - this.headerState = 'touched'; + this.headerState = "touched"; if (this.knob) { - this.knob.setNativeProps({style: { opacity: 0.5 }}); + this.knob.setNativeProps({ style: { opacity: 0.5 } }); } } onTouchEnd() { if (this.knob) { - this.knob.setNativeProps({style: { opacity: 1 }}); + this.knob.setNativeProps({ style: { opacity: 1 } }); } - if (this.headerState === 'touched') { + if (this.headerState === "touched") { this.setScrollPadPosition(0, true); this.enableCalendarScrolling(); } - this.headerState = 'idle'; + this.headerState = "idle"; } onStartDrag() { - this.headerState = 'dragged'; + this.headerState = "dragged"; this.knobTracker.reset(); } @@ -175,9 +169,9 @@ export default class AgendaView extends Component { this.onTouchEnd(); const currentY = e.nativeEvent.contentOffset.y; this.knobTracker.add(currentY); - const projectedY = currentY + this.knobTracker.estimateSpeed() * 250/*ms*/; + const projectedY = currentY + this.knobTracker.estimateSpeed() * 250; /*ms*/ const maxY = this.initialScrollPadPosition(); - const snapY = (projectedY > maxY / 2) ? maxY : 0; + const snapY = projectedY > maxY / 2 ? maxY : 0; this.setScrollPadPosition(snapY, true); if (snapY === 0) { this.enableCalendarScrolling(); @@ -196,14 +190,20 @@ export default class AgendaView extends Component { } loadReservations(props) { - if ((!props.items || !Object.keys(props.items).length) && !this.state.firstResevationLoad) { - this.setState({ - firstResevationLoad: true - }, () => { - if (this.props.loadItemsForMonth) { - this.props.loadItemsForMonth(xdateToData(this.state.selectedDay)); + if ( + (!props.items || !Object.keys(props.items).length) && + !this.state.firstResevationLoad + ) { + this.setState( + { + firstResevationLoad: true, + }, + () => { + if (this.props.loadItemsForMonth) { + this.props.loadItemsForMonth(xdateToData(this.state.selectedDay)); + } } - }); + ); } } @@ -219,7 +219,7 @@ export default class AgendaView extends Component { componentWillReceiveProps(props) { if (props.items) { this.setState({ - firstResevationLoad: false + firstResevationLoad: false, }); } else { this.loadReservations(props); @@ -228,7 +228,7 @@ export default class AgendaView extends Component { enableCalendarScrolling() { this.setState({ - calendarScrollable: true + calendarScrollable: true, }); if (this.props.onCalendarToggled) { this.props.onCalendarToggled(true); @@ -241,7 +241,11 @@ export default class AgendaView extends Component { // in CalendarList listView, but that might impact performance when scrolling // month list in expanded CalendarList. // Further info https://github.com/facebook/react-native/issues/1831 - this.calendar.scrollToDay(this.state.selectedDay, this.calendarOffset() + 1, true); + this.calendar.scrollToDay( + this.state.selectedDay, + this.calendarOffset() + 1, + true + ); } _chooseDayFromCalendar(d) { @@ -252,14 +256,14 @@ export default class AgendaView extends Component { const day = parseDate(d); this.setState({ calendarScrollable: false, - selectedDay: day.clone() + selectedDay: day.clone(), }); if (this.props.onCalendarToggled) { this.props.onCalendarToggled(false); } if (!optimisticScroll) { this.setState({ - topDay: day.clone() + topDay: day.clone(), }); } this.setScrollPadPosition(this.initialScrollPadPosition(), true); @@ -288,7 +292,7 @@ export default class AgendaView extends Component { topDay={this.state.topDay} onDayChange={this.onDayChange.bind(this)} onScroll={() => {}} - ref={(c) => this.list = c} + ref={(c) => (this.list = c)} theme={this.props.theme} /> ); @@ -299,7 +303,7 @@ export default class AgendaView extends Component { const withAnimation = dateutils.sameMonth(newDate, this.state.selectedDay); this.calendar.scrollToDay(day, this.calendarOffset(), withAnimation); this.setState({ - selectedDay: parseDate(day) + selectedDay: parseDate(day), }); if (this.props.onDayChange) { @@ -311,42 +315,55 @@ export default class AgendaView extends Component { let markings = this.props.markedDates; if (!markings) { markings = {}; - Object.keys(this.props.items || {}).forEach(key => { + Object.keys(this.props.items || {}).forEach((key) => { if (this.props.items[key] && this.props.items[key].length) { - markings[key] = {marked: true}; + markings[key] = { marked: true }; } }); } - const key = this.state.selectedDay.toString('yyyy-MM-dd'); - return {...markings, [key]: {...(markings[key] || {}), ...{selected: true}}}; + const key = this.state.selectedDay.toString("yyyy-MM-dd"); + return { + ...markings, + [key]: { ...(markings[key] || {}), ...{ selected: true } }, + }; } render() { const agendaHeight = Math.max(0, this.viewHeight - HEADER_HEIGHT); const weekDaysNames = dateutils.weekDayNames(this.props.firstDay); - const weekdaysStyle = [this.styles.weekdays, { - opacity: this.state.scrollY.interpolate({ - inputRange: [agendaHeight - HEADER_HEIGHT, agendaHeight], - outputRange: [0, 1], - extrapolate: 'clamp', - }), - transform: [{ translateY: this.state.scrollY.interpolate({ - inputRange: [Math.max(0, agendaHeight - HEADER_HEIGHT), agendaHeight], - outputRange: [-HEADER_HEIGHT, 0], - extrapolate: 'clamp', - })}] - }]; + const weekdaysStyle = [ + this.styles.weekdays, + { + opacity: this.state.scrollY.interpolate({ + inputRange: [agendaHeight - HEADER_HEIGHT, agendaHeight], + outputRange: [0, 1], + extrapolate: "clamp", + }), + transform: [ + { + translateY: this.state.scrollY.interpolate({ + inputRange: [ + Math.max(0, agendaHeight - HEADER_HEIGHT), + agendaHeight, + ], + outputRange: [-HEADER_HEIGHT, 0], + extrapolate: "clamp", + }), + }, + ], + }, + ]; const headerTranslate = this.state.scrollY.interpolate({ inputRange: [0, agendaHeight], outputRange: [agendaHeight, 0], - extrapolate: 'clamp', + extrapolate: "clamp", }); const contentTranslate = this.state.scrollY.interpolate({ inputRange: [0, agendaHeight], - outputRange: [0, agendaHeight/2], - extrapolate: 'clamp', + outputRange: [0, agendaHeight / 2], + extrapolate: "clamp", }); const headerStyle = [ @@ -356,48 +373,63 @@ export default class AgendaView extends Component { if (!this.state.calendarIsReady) { // limit header height until everything is setup for calendar dragging - headerStyle.push({height: 0}); + headerStyle.push({ height: 0 }); // fill header with appStyle.calendarBackground background to reduce flickering - weekdaysStyle.push({height: HEADER_HEIGHT}); + weekdaysStyle.push({ height: HEADER_HEIGHT }); } - const shouldAllowDragging = !this.props.hideKnob && !this.state.calendarScrollable; - const scrollPadPosition = (shouldAllowDragging ? HEADER_HEIGHT : 0) - KNOB_HEIGHT; + const shouldAllowDragging = + !this.props.hideKnob && !this.state.calendarScrollable; + const scrollPadPosition = + (shouldAllowDragging ? HEADER_HEIGHT : 0) - KNOB_HEIGHT; const scrollPadStyle = { - position: 'absolute', + position: "absolute", width: 80, height: KNOB_HEIGHT, top: scrollPadPosition, left: (this.viewWidth - 80) / 2, }; - let knob = (); + let knob = ; if (!this.props.hideKnob) { - const knobView = this.props.renderKnob ? this.props.renderKnob() : (); + const knobView = this.props.renderKnob ? ( + this.props.renderKnob() + ) : ( + + ); knob = this.state.calendarScrollable ? null : ( - this.knob = c}>{knobView} + (this.knob = c)}>{knobView} ); } return ( - + {this.renderReservations()} - + { - this.calendar.scrollToDay(this.state.selectedDay.clone(), this.calendarOffset(), false); + this.calendar.scrollToDay( + this.state.selectedDay.clone(), + this.calendarOffset(), + false + ); }} calendarWidth={this.viewWidth} theme={this.props.theme} onVisibleMonthsChange={this.onVisibleMonthsChange.bind(this)} - ref={(c) => this.calendar = c} + ref={(c) => (this.calendar = c)} minDate={this.props.minDate} maxDate={this.props.maxDate} current={this.currentMonth} @@ -420,14 +452,27 @@ export default class AgendaView extends Component { {knob} - {this.props.showWeekNumbers && } + {this.props.showWeekNumbers && ( + + )} {weekDaysNames.map((day, index) => ( - {day} + + {day} + ))} this.scrollPad = c} - overScrollMode='never' + ref={(c) => (this.scrollPad = c)} + overScrollMode="never" showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} style={scrollPadStyle} @@ -439,10 +484,13 @@ export default class AgendaView extends Component { onScrollEndDrag={this.onSnapAfterDrag} onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }], - { useNativeDriver: true }, + { useNativeDriver: true } )} > - + ); diff --git a/src/calendar/index.js b/src/calendar/index.js index d0e8bc6e8f..a084bb9d80 100644 --- a/src/calendar/index.js +++ b/src/calendar/index.js @@ -1,25 +1,25 @@ -import React, {Component} from 'react'; +import React, { Component } from "react"; import { View, ViewPropTypes, ScrollView, Dimensions, ActivityIndicator, - Platform -} from 'react-native'; -import PropTypes from 'prop-types'; - -import XDate from 'xdate'; -import dateutils from '../dateutils'; -import {xdateToData, parseDate} from '../interface'; -import styleConstructor from './style'; -import Day from './day/basic'; -import UnitDay from './day/period'; -import MultiDotDay from './day/multi-dot'; -import MultiPeriodDay from './day/multi-period'; -import SingleDay from './day/custom'; -import CalendarHeader from './header'; -import shouldComponentUpdate from './updater'; + Platform, +} from "react-native"; +import PropTypes from "prop-types"; + +import XDate from "xdate"; +import dateutils from "../dateutils"; +import { xdateToData, parseDate } from "../interface"; +import styleConstructor from "./style"; +import Day from "./day/basic"; +import UnitDay from "./day/period"; +import MultiDotDay from "./day/multi-dot"; +import MultiPeriodDay from "./day/multi-period"; +import SingleDay from "./day/custom"; +import CalendarHeader from "./header"; +import shouldComponentUpdate from "./updater"; //Fallback when RN version is < 0.44 const viewPropTypes = ViewPropTypes || View.propTypes; @@ -35,7 +35,7 @@ let onPressArrowRightTriggered = false; // to throttle back-to-back triggering of onPressArrowLeft in horizontal calendar let onPressArrowLeftTriggered = false; -const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000; +const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000; class Calendar extends Component { static propTypes = { @@ -45,7 +45,7 @@ class Calendar extends Component { markedDates: PropTypes.object, // Specify style for calendar container element. Default = {} - style: viewPropTypes.style, + style: PropTypes.style, // Initially visible month. Default = Date() current: PropTypes.any, // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined @@ -114,7 +114,7 @@ class Calendar extends Component { // to show a loader loading: PropTypes.bool, // provide a custom loader component - LoaderComponent: PropTypes.any + LoaderComponent: PropTypes.any, }; constructor(props) { @@ -128,7 +128,7 @@ class Calendar extends Component { } this.state = { currentMonth, - horizontal: props.horizontal + horizontal: props.horizontal, }; this.updateMonth = this.updateMonth.bind(this); @@ -142,56 +142,72 @@ class Calendar extends Component { } componentWillReceiveProps(nextProps) { - const current= parseDate(nextProps.current); - if (current && current.toString('yyyy MM') !== this.state.currentMonth.toString('yyyy MM')) { + const current = parseDate(nextProps.current); + if ( + current && + current.toString("yyyy MM") !== + this.state.currentMonth.toString("yyyy MM") + ) { this.setState({ - currentMonth: current.clone() + currentMonth: current.clone(), }); } this.setState({ - horizontal: nextProps.horizontal + horizontal: nextProps.horizontal, }); } // scroll the horizontal calendar so that selected date is visible componentDidUpdate() { const horizontalScrollView = this.horizontalScrollViewRef.current; - if (horizontalScrollView - && this.props.autoHorizontalScroll - && (this.props.showPastDatesInHorizontal === undefined)) { - const windowWidth = Dimensions.get('window').width; + if ( + horizontalScrollView && + this.props.autoHorizontalScroll && + this.props.showPastDatesInHorizontal === undefined + ) { + const windowWidth = Dimensions.get("window").width; horizontalScrollView.scrollTo({ x: horizontalScrollViewOffset * windowWidth, - animated: true + animated: true, }); } } updateMonth(day, doNotTriggerListeners) { - if (day.toString('yyyy MM') === this.state.currentMonth.toString('yyyy MM')) { + if ( + day.toString("yyyy MM") === this.state.currentMonth.toString("yyyy MM") + ) { return; } - this.setState({ - currentMonth: day.clone() - }, () => { - if (!doNotTriggerListeners) { - const currMont = this.state.currentMonth.clone(); - if (this.props.onMonthChange) { - this.props.onMonthChange(xdateToData(currMont)); - } - if (this.props.onVisibleMonthsChange) { - this.props.onVisibleMonthsChange([xdateToData(currMont)]); + this.setState( + { + currentMonth: day.clone(), + }, + () => { + if (!doNotTriggerListeners) { + const currMont = this.state.currentMonth.clone(); + if (this.props.onMonthChange) { + this.props.onMonthChange(xdateToData(currMont)); + } + if (this.props.onVisibleMonthsChange) { + this.props.onVisibleMonthsChange([xdateToData(currMont)]); + } } } - }); + ); } _handleDayInteraction(date, interaction) { const day = parseDate(date); const minDate = parseDate(this.props.minDate); const maxDate = parseDate(this.props.maxDate); - if (!(minDate && !dateutils.isGTE(day, minDate)) && !(maxDate && !dateutils.isLTE(day, maxDate))) { - const shouldUpdateMonth = this.props.disableMonthChange === undefined || !this.props.disableMonthChange; + if ( + !(minDate && !dateutils.isGTE(day, minDate)) && + !(maxDate && !dateutils.isLTE(day, maxDate)) + ) { + const shouldUpdateMonth = + this.props.disableMonthChange === undefined || + !this.props.disableMonthChange; if (shouldUpdateMonth) { this.updateMonth(day); } @@ -216,25 +232,31 @@ class Calendar extends Component { renderDay(day, id) { const minDate = parseDate(this.props.minDate); const maxDate = parseDate(this.props.maxDate); - let state = ''; + let state = ""; if (this.props.disabledByDefault) { - state = 'disabled'; - } else if ((minDate && !dateutils.isGTE(day, minDate)) || (maxDate && !dateutils.isLTE(day, maxDate))) { - state = 'disabled'; + state = "disabled"; + } else if ( + (minDate && !dateutils.isGTE(day, minDate)) || + (maxDate && !dateutils.isLTE(day, maxDate)) + ) { + state = "disabled"; } else if (!dateutils.sameMonth(day, this.state.currentMonth)) { - state = 'disabled'; + state = "disabled"; } else if (dateutils.sameDate(day, XDate())) { - state = 'today'; + state = "today"; } - if (!dateutils.sameMonth(day, this.state.currentMonth) && this.props.hideExtraDays) { - return (); + if ( + !dateutils.sameMonth(day, this.state.currentMonth) && + this.props.hideExtraDays + ) { + return ; } const DayComp = this.getDayComponent(); const date = day.getDate(); return ( - + ); + {this.showCalendar(weeks)} + + ); } }