diff --git a/README.md b/README.md index f5e13fab..4d68347d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Props * **`events`** _(Array)_ - Events to display * **`onEventPress`** _(Function)_ - Callback when event item is clicked -* **`numberOfDays`** _(Number)_ - Set number of days to show in view, can be `1`, `3`, `7`. +* **`numberOfDays`** _(Number)_ - Set number of days to show in view, can be `1`, `3`, `5`, `7`. * **`formatDateHeader`** _(String)_ - Format for dates of header, default is `MMM D` * **`selectedDate`** _(Date)_ - Intial date to show week/days in view * **`onSwipeNext`** _(Function)_ - Callback when calendar is swiped to next week/days diff --git a/images/gif.gif b/images/gif.gif index fa013e14..071c1448 100644 Binary files a/images/gif.gif and b/images/gif.gif differ diff --git a/src/Header/Header.js b/src/Header/Header.js index 7ec014e1..363387e0 100644 --- a/src/Header/Header.js +++ b/src/Header/Header.js @@ -2,18 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Text, View } from 'react-native'; -import { getFormattedDate, getCurrentMonth, calculateDaysArray } from '../utils'; - +import { getFormattedDate, calculateDaysArray } from '../utils'; import styles from './Header.styles'; -const getFontSizeHeader = (numberOfDays) => { - if (numberOfDays > 1) { - return 12; - } - - return 16; -}; - const getDayTextStyles = (numberOfDays) => { const fontSize = numberOfDays === 7 ? 12 : 14; return { @@ -59,29 +50,17 @@ const Columns = ({ ); }; -const Title = ({ numberOfDays, selectedDate, textColor }) => { // eslint-disable-line react/prop-types - return ( - - - {getCurrentMonth(selectedDate)} - - - ); -}; const WeekViewHeader = ({ numberOfDays, - selectedDate, + initialDate, formatDate, style, textColor, }) => { - const columns = calculateDaysArray(selectedDate, numberOfDays); + const columns = calculateDaysArray(initialDate, numberOfDays); return ( - {columns && <Columns format={formatDate} columns={columns} numberOfDays={numberOfDays} textColor={textColor} />} </View> ); @@ -89,7 +68,7 @@ const WeekViewHeader = ({ WeekViewHeader.propTypes = { numberOfDays: PropTypes.oneOf([1, 3, 5, 7]).isRequired, - selectedDate: PropTypes.instanceOf(Date).isRequired, + initialDate: PropTypes.string.isRequired, formatDate: PropTypes.string, style: PropTypes.object, textColor: PropTypes.string, diff --git a/src/Header/Header.styles.js b/src/Header/Header.styles.js index 6ae36497..03395754 100644 --- a/src/Header/Header.styles.js +++ b/src/Header/Header.styles.js @@ -6,13 +6,6 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'space-between', }, - title: { - justifyContent: 'center', - alignItems: 'center', - width: 60, - borderColor: '#fff', - borderTopWidth: 1, - }, columns: { flex: 1, flexDirection: 'row', diff --git a/src/Title/Title.js b/src/Title/Title.js new file mode 100644 index 00000000..db25cf9d --- /dev/null +++ b/src/Title/Title.js @@ -0,0 +1,39 @@ +import React from 'react'; +import { Text, View } from 'react-native'; +import PropTypes from 'prop-types'; + +import { getCurrentMonth } from '../utils'; +import styles from './Title.styles'; + +const getFontSizeHeader = (numberOfDays) => { + if (numberOfDays > 1) { + return 12; + } + return 16; +}; + +const Title = ({ + style, + numberOfDays, + selectedDate, + textColor, +}) => { + return ( + <View style={[styles.title, style]}> + <Text + style={{ color: textColor, fontSize: getFontSizeHeader(numberOfDays) }} + > + {getCurrentMonth(selectedDate)} + </Text> + </View> + ); +}; + +Title.propTypes = { + numberOfDays: PropTypes.oneOf([1, 3, 5, 7]).isRequired, + selectedDate: PropTypes.instanceOf(Date).isRequired, + style: PropTypes.object, + textColor: PropTypes.string, +}; + +export default React.memo(Title); diff --git a/src/Title/Title.styles.js b/src/Title/Title.styles.js new file mode 100644 index 00000000..2f8e5bac --- /dev/null +++ b/src/Title/Title.styles.js @@ -0,0 +1,13 @@ +import { StyleSheet } from 'react-native'; + +const styles = StyleSheet.create({ + title: { + justifyContent: 'center', + alignItems: 'center', + width: 60, + borderColor: '#fff', + borderTopWidth: 1, + }, +}); + +export default styles; diff --git a/src/WeekView/WeekView.js b/src/WeekView/WeekView.js index bba2c71c..9c3127fd 100644 --- a/src/WeekView/WeekView.js +++ b/src/WeekView/WeekView.js @@ -4,6 +4,7 @@ import { View, ScrollView, Dimensions, + Animated, } from 'react-native'; import moment from 'moment'; import memoizeOne from 'memoize-one'; @@ -11,6 +12,7 @@ import memoizeOne from 'memoize-one'; import Event from '../Event/Event'; import Events from '../Events/Events'; import Header from '../Header/Header'; +import Title from '../Title/Title'; import Times from '../Times/Times'; import styles from './WeekView.styles'; import { @@ -31,12 +33,14 @@ export default class WeekView extends Component { }; this.eventsGrid = null; this.verticalAgenda = null; - setLocale(props.locale); - + this.header = null; this.pagesLeft = 2; this.pagesRight = 2; this.currentPageIndex = 2; this.totalPages = this.pagesLeft + this.pagesRight + 1; + this.eventsGridScrollX = new Animated.Value(0); + + setLocale(props.locale); } componentDidMount() { @@ -44,6 +48,9 @@ export default class WeekView extends Component { this.eventsGrid.scrollTo({ y: 0, x: 2 * (SCREEN_WIDTH - 60), animated: false }); this.scrollToAgendaStart(); }); + this.eventsGridScrollX.addListener((position) => { + this.header.scrollTo({ x: position.value, animated: false }); + }); } componentDidUpdate(prevprops) { @@ -57,12 +64,8 @@ export default class WeekView extends Component { this.eventsGrid.scrollTo({ y: 0, x: 2 * (SCREEN_WIDTH - 60), animated: false }); } - scrollToAgendaStart = () => { - if (this.verticalAgenda) { - const { startHour, hoursInDisplay } = this.props; - const startHeight = startHour * CONTAINER_HEIGHT / hoursInDisplay; - this.verticalAgenda.scrollTo({ y: startHeight, x: 0, animated: false }); - } + componentWillUnmount() { + this.eventsGridScrollX.removeAllListeners(); } calculateTimes = memoizeOne((hoursInDisplay) => { @@ -79,6 +82,14 @@ export default class WeekView extends Component { return times; }); + scrollToAgendaStart = () => { + if (this.verticalAgenda) { + const { startHour, hoursInDisplay } = this.props; + const startHeight = startHour * CONTAINER_HEIGHT / hoursInDisplay; + this.verticalAgenda.scrollTo({ y: startHeight, x: 0, animated: false }); + } + } + scrollEnded = (event) => { const { nativeEvent: { contentOffset, contentSize } } = event; const { x: position } = contentOffset; @@ -113,6 +124,10 @@ export default class WeekView extends Component { this.verticalAgenda = ref; } + headerRef = (ref) => { + this.header = ref; + }; + calculatePagesDates = memoizeOne((currentMoment, numberOfDays) => { const initialDates = []; for (let i = -this.pagesLeft; i <= this.pagesRight; i += 1) { @@ -176,14 +191,32 @@ export default class WeekView extends Component { const eventsByDate = this.sortEventsByDate(events); return ( <View style={styles.container}> - <View style={styles.header}> - <Header + <View style={styles.headerContainer}> + <Title style={headerStyle} - textColor={headerTextColor} - formatDate={formatDateHeader} - selectedDate={currentMoment} numberOfDays={numberOfDays} + selectedDate={currentMoment} + textColor={headerTextColor} /> + <ScrollView + horizontal + pagingEnabled + scrollEnabled={false} + automaticallyAdjustContentInsets={false} + ref={this.headerRef} + > + {initialDates.map(date => ( + <View key={date} style={styles.header}> + <Header + style={headerStyle} + textColor={headerTextColor} + formatDate={formatDateHeader} + initialDate={date} + numberOfDays={numberOfDays} + /> + </View> + ))} + </ScrollView> </View> <ScrollView ref={this.verticalAgendaRef} @@ -195,6 +228,15 @@ export default class WeekView extends Component { pagingEnabled automaticallyAdjustContentInsets={false} onMomentumScrollEnd={this.scrollEnded} + scrollEventThrottle={32} + onScroll={Animated.event([{ + nativeEvent: { + contentOffset: { + x: this.eventsGridScrollX, + }, + }, + }, + ], { useNativeDriver: false })} ref={this.eventsGridRef} > {initialDates.map(date => ( diff --git a/src/WeekView/WeekView.styles.js b/src/WeekView/WeekView.styles.js index d8047459..9f9e375b 100644 --- a/src/WeekView/WeekView.styles.js +++ b/src/WeekView/WeekView.styles.js @@ -1,4 +1,6 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Dimensions } from 'react-native'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); const styles = StyleSheet.create({ container: { @@ -7,10 +9,15 @@ const styles = StyleSheet.create({ scrollViewContent: { flexDirection: 'row', }, + headerContainer: { + flexDirection: 'row', + }, header: { + flex: 1, height: 50, justifyContent: 'center', alignItems: 'center', + width: SCREEN_WIDTH - 60, }, }); diff --git a/src/utils.js b/src/utils.js index 6b836213..fc0acde2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -34,6 +34,5 @@ export const calculateDaysArray = (date, numberOfDays) => { const currentDate = moment(date).add(i, 'd'); dates.push(currentDate); } - return dates; -} +};