Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //@flow
- import React, {Component} from 'react';
- import {ScrollView, StyleSheet, Dimensions} from 'react-native';
- import {Text, View, Header, Left} from 'native-base';
- import moment from 'moment';
- import Dates from './Dates';
- import type Moment from 'moment';
- var plLocale = require('moment/locale/pl');
- moment.locale('pl', plLocale);
- type Props = {
- //optional prop to pass custom date instead of today
- currentDate?: string | Moment,
- //callback executed when user taps on a date
- onSelectDate: (date: Moment) => any,
- //number of days to show before today/current chosen date
- showDaysBeforeCurrent?: number,
- //number of days to show after
- showDaysAfterCurrent?: number,
- };
- type State = {
- //true when all dates have been rendered
- allDatesHaveRendered: boolean,
- //current chosen date index
- currentDateIndex: ?number,
- //Store months and years visible on screen
- //for rendering months and years above the dates
- visibleMonths: ?Array<string>,
- visibleYears: ?Array<string>,
- // array of dates to show
- dates: Array<Moment>,
- //store each day width to help with scrolling to specific days
- //and calculating which days are visible on the screen
- dayWidths: ?{|[index: number]: number|},
- // store current scroll position
- scrollPositionX: number,
- };
- const {width: screenWidth} = Dimensions.get('window');
- const formatMonth = (date: Moment): string => date.format('MMMM');
- const formatYear = (date: Moment): string => date.format('YYYY');
- export default class Calendar extends Component {
- props: Props;
- state: State;
- static defaultProps = {
- //show 5 days before current date
- showDaysBeforeCurrent: 5,
- // ^ but after
- showDaysAfterCurrent: 5,
- };
- _scrollView;
- //initialize state with default values
- constructor(props: Props) {
- super(props);
- this.state = {
- allDatesHaveRendered: false,
- currentDateIndex: props.showDaysBeforeCurrent,
- dates: this.getDates(),
- dayWidths: undefined,
- scrollPositionX: 0,
- visibleMonths: undefined,
- visibleYears: undefined,
- };
- }
- //get an array of dates for showing in horizontal screen view
- getDates = (): Array<Moment> => {
- const {
- currentDate,
- showDaysBeforeCurrent,
- showDaysAfterCurrent,
- } = this.props;
- // 'showDaysBeforeCurrent' ago before current chosen or todays date
- const startDay = moment(currentDate || undefined).subtract(
- showDaysBeforeCurrent + 1,
- 'days',
- );
- //number of days in total
- const totalDaysCount = showDaysBeforeCurrent + showDaysAfterCurrent + 1;
- // and return array of 'totalDaysCount' dates
- //console.log(totalDaysCount)
- return [...Array(totalDaysCount)].map(_ => startDay.add(1, 'day').clone());
- };
- //return a subset of days currently visible on screen
- getVisibleDates = (): ?Array<Moment> => {
- const {dates, dayWidths, scrollPositionX} = this.state;
- if (!dayWidths) {
- return;
- }
- let datePositionX = 0;
- let firstVisibleDateIndex = undefined;
- let lastVisibleDateIndex = undefined;
- //iterate through daywidths to $FlowFixMe
- Object.values(dayWidths).some((width: number, index: number) => {
- if (
- firstVisibleDateIndex === undefined &&
- datePositionX >= scrollPositionX
- ) {
- firstVisibleDateIndex = index > 0 ? index - 1 : index;
- }
- if (
- lastVisibleDateIndex === undefined &&
- datePositionX >= scrollPositionX + screenWidth
- ) {
- lastVisibleDateIndex = index;
- }
- datePositionX += width;
- return !!(firstVisibleDateIndex && lastVisibleDateIndex);
- });
- return dates.slice(firstVisibleDateIndex, lastVisibleDateIndex);
- };
- getVisibleMonthAndYear = (): ?string => {
- const {dates, visibleMonths, visibleYears} = this.state;
- //no visible month or year yet
- if (!visibleMonths || !visibleYears) {
- //return the month and year of the very first date
- if (dates) {
- const firstDate = dates[0];
- return `${formatMonth(firstDate)}, ${formatYear(firstDate)}`;
- }
- return undefined;
- }
- //one or two months within the same year
- if (visibleYears.length === 1) {
- return `${visibleMonths.join(' - ')}, ${visibleYears[0]}`;
- }
- //two months within the same year
- return visibleMonths
- .map((month, index) => `${month}, ${visibleYears[index]}`)
- .join(' - ');
- };
- updateVisibleMonthAndYear = () => {
- const {allDatesHaveRendered} = this.state;
- if (!allDatesHaveRendered) {
- return;
- }
- const visibleDates = this.getVisibleDates();
- if (!visibleDates) {
- return;
- }
- let visibleMonths = [];
- let visibleYears = [];
- visibleDates.forEach((date: Moment) => {
- const month = formatMonth(date);
- const year = formatYear(date);
- if (!visibleMonths.includes(month)) {
- visibleMonths.push(month);
- }
- if (!visibleYears.includes(year)) {
- visibleYears.push(year);
- }
- });
- this.setState({
- visibleMonths,
- visibleYears,
- });
- };
- scrollToCurrentDay = () => {
- const {allDatesHaveRendered, currentDateIndex, dayWidths} = this.state;
- //making sure values are correct
- if (
- !allDatesHaveRendered ||
- currentDateIndex === undefined ||
- currentDateIndex === null
- ) {
- return;
- }
- //put all widths value into simple array $FlowFixMe
- const dayWidthsArray: Array<number> = Object.values(dayWidths);
- // total width all days take
- const allDaysWidths = dayWidthsArray.reduce(
- (total, width) => width + total,
- 0,
- );
- //current day button width
- const currentDayWidth = dayWidthsArray[currentDateIndex];
- //minimal x value to prevent scrolling before first day
- const minX = 0;
- //maximum x value to prevent scrolling after last day
- const maxX = allDaysWidths > screenWidth ? allDaysWidths - screenWidth : 0; // no scrolling if nowhere to scroll
- let scrollToX;
- scrollToX =
- dayWidthsArray
- //get all days before target one
- .slice(0, currentDateIndex + 1)
- // and calculate total width
- .reduce((total, width) => width + total, 0) -
- //subtract half of the screen so target day is centered
- screenWidth / 2 -
- currentDayWidth / 2;
- //do not scroll over left ledge
- if (scrollToX < minX) {
- scrollToX = 0;
- } else if (scrollToX > maxX) {
- scrollToX = maxX;
- }
- this._scrollView.scrollTo({x: scrollToX});
- };
- onSelectDay = (index: number) => {
- const {dates} = this.state;
- const {onSelectDate} = this.props;
- this.setState({currentDateIndex: index}, this.scrollToCurrentDay);
- onSelectDate(dates[index]);
- };
- onRenderDay = (index: number, width: number) => {
- const {dayWidths} = this.state;
- const {showDaysBeforeCurrent, showDaysAfterCurrent} = this.props;
- //check whether all dates were rendered correctly
- const allDatesHaveRendered =
- dayWidths &&
- Object.keys(dayWidths).length >=
- showDaysBeforeCurrent + showDaysAfterCurrent;
- this.setState(
- prevState => ({
- allDatesHaveRendered,
- dayWidths: {
- //keep all existing widths added previously
- ...prevState.dayWidths,
- //keep index for calculating scrolling position for each day
- [index]: width,
- },
- }),
- () => {
- if (allDatesHaveRendered) {
- this.scrollToCurrentDay();
- this.updateVisibleMonthAndYear();
- }
- },
- );
- };
- onScroll = (event: {
- nativeEvent: {contentOffset: {x: number, y: number}},
- }) => {
- const {
- nativeEvent: {
- contentOffset: {x},
- },
- } = event;
- this.setState({scrollPositionX: x}, this.updateVisibleMonthAndYear);
- };
- render() {
- const {dates, currentDateIndex} = this.state;
- const visibleMonthAndYear = this.getVisibleMonthAndYear();
- return (
- <View>
- <View>
- <Text style={styles.visibleMonthAndYear}>{visibleMonthAndYear}</Text>
- </View>
- <ScrollView
- ref={scrollView => {
- this._scrollView = scrollView;
- }}
- horizontal={true}
- showHorizontalScrollIndicator={false}
- automaticallyAdjustContentInsets={false} //disables automatic content adjusting
- scrollEventThrottle={100}
- onScroll={this.onScroll}>
- <Dates
- dates={dates}
- currentDateIndex={currentDateIndex}
- onSelectDay={this.onSelectDay}
- onRenderDay={this.onRenderDay}
- />
- </ScrollView>
- </View>
- );
- }
- }
- const styles = StyleSheet.create({
- visibleMonthAndYear: {
- color: 'black', // '#ff8000',//'rgba(255, 255, 255, 0.5)',
- fontSize: 20,
- left: 15,
- bottom: 15,
- top: 1,
- //paddingHorizontal: 25,
- //position: 'absolute',
- textAlign: 'left',
- },
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement