Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 1, 2023 10:52 pm GMT

Horizontal Calendar - a simple date picker for React Native

Trailer

I decided to write this to help someone who may need to build a simple horizontal calendar picker for your mobile app. Today we are going to do so without having to use the cumbersome third party packages.

Why?

Well, suppose you're building an app that requires you(or the user) to select a date and other events that would be dependent on the date selected e.g. movie booking, event planner, travel/event planner, flight/bus booking or even an appointment manager(which I'll be launching in a few weeks)

Okay, that's enough foreplay; lets dive into the cool stuff. I'll add a repo at the end.

Let the fun begin

Create a new project.

Initialize a new expo app - We are using Expo because its 2023 and its freaking cool.

npx create-expo-app HzCalendarcd HzCalendar

Now you can run any of the following commands depending on which device/simulator you're testing with. As an added bonus, you can run it on web too(expo is that good)

npm run androidnpm run iosnpm run web

First

Install dependencies

We're only going to need only moment for working with dates. You may choose to use others like date-fns if you wish.

npm install --save moment

1. Calendar Component

We are going to have a calendar component that takes two props, an onSelectDate and selected. The selected prop is so we change styles later. Lets create a components folder, then create a Calendar component.

  • we are going to track 3 states: an empty date to start with, a scrollPosition and a currentMonth.
  • later we will use the scrollPosition to track the current month.

First we are gonna use a for loop to generate the next 10 days. We will then parse the days with moment and update the dates state.

At this point I assume you understand the basic javascript functions, and how react works when passing props and state data. I'll also add a few styles, so I hope you understand how styling works in React Native.

Then we will display the dates in a horizontal scrollview.

2. Date Component

We need to abstract the date component. Lets create a Date component in the Components folder

  • This will be a simple TouchableOpacity button that will call onSelectDate
  • Our Date component will take 3 props, a date that will be displayed, the onSelectDate function we passed to calendar, and the selected prop too.
  • We'll use the selected prop to give the selected date different styling.
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'import moment from 'moment'const Date = ({ date, onSelectDate, selected }) => {  /**   * use moment to compare the date to today   * if today, show 'Today'   * if not today, show day of the week e.g 'Mon', 'Tue', 'Wed'   */  const day = moment(date).format('YYYY-MM-DD') === moment().format('YYYY-MM-DD') ? 'Today' : moment(date).format('ddd')  // get the day number e.g 1, 2, 3, 4, 5, 6, 7  const dayNumber = moment(date).format('D')  // get the full date e.g 2021-01-01 - we'll use this to compare the date to the selected date  const fullDate = moment(date).format('YYYY-MM-DD')  return (    <TouchableOpacity      onPress={() => onSelectDate(fullDate)}      style={[styles.card, selected === fullDate && { backgroundColor: "#6146c6" }]}    >      <Text        style={[styles.big, selected === fullDate && { color: "#fff" }]}      >        {day}      </Text>      <View style={{ height: 10 }} />      <Text        style={[          styles.medium,          selected === fullDate && { color: "#fff", fontWeight: 'bold', fontSize: 24 },        ]}      >        {dayNumber}      </Text>    </TouchableOpacity>  )}export default Dateconst styles = StyleSheet.create({  card: {    backgroundColor: '#eee',    borderRadius: 10,    borderColor: '#ddd',    padding: 10,    marginVertical: 10,    alignItems: 'center',    height: 90,    width: 80,    marginHorizontal: 5,  },  big: {    fontWeight: 'bold',    fontSize: 20,  },  medium: {    fontSize: 16,  },})

Then we call the Date component in the Calendar component and pass each date in the scrollview.

import { useState, useEffect } from 'react'import { StyleSheet, Text, View, ScrollView } from 'react-native'import moment from 'moment'import Date from './Date'const Calendar = ({ onSelectDate, selected }) => {  const [dates, setDates] = useState([])  const [scrollPosition, setScrollPosition] = useState(0)  const [currentMonth, setCurrentMonth] = useState()  // get the dates from today to 10 days from now, format them as strings and store them in state  const getDates = () => {    const _dates = []    for (let i = 0; i < 10; i++) {      const date = moment().add(i, 'days')      _dates.push(date)    }    setDates(_dates)  }  useEffect(() => {    getDates()  }, [])  return (    <>      <View style={styles.centered}>        <Text style={styles.title}>Current month</Text>      </View>      <View style={styles.dateSection}>        <View style={styles.scroll}>          <ScrollView            horizontal            showsHorizontalScrollIndicator={false}          >            {dates.map((date, index) => (              <Date                key={index}                date={date}                onSelectDate={onSelectDate}                selected={selected}              />            ))}          </ScrollView>        </View>      </View>    </>  )}export default Calendarconst styles = StyleSheet.create({  centered: {    justifyContent: 'center',    alignItems: 'center',  },  title: {    fontSize: 20,    fontWeight: 'bold',  },  dateSection: {    width: '100%',    padding: 20,  },  scroll: {    height: 150,  },})

Now we update our App.js file to display the Calendar component.

  • We'll add a selectedDate state that we shall pass to Calendar props.
import { StatusBar } from 'expo-status-bar';import { useState } from 'react';import { StyleSheet, View } from 'react-native';import Calendar from './components/Calendar';export default function App() {  const [selectedDate, setSelectedDate] = useState(null);  return (    <View style={styles.container}>      <Calendar onSelectDate={setSelectedDate} selected={selectedDate} />      <StatusBar style="auto" />    </View>  );}const styles = StyleSheet.create({  container: {    flex: 1,    backgroundColor: '#fff',    alignItems: 'center',    justifyContent: 'center',  },});

Now we have a working Horizontal scroll calendar component that looks pretty much like this.(if you select a date, you'll notice the styling changes - that's that Stylesheet magic we added in the Date component)

almost there

Bonus - Current month

Now let's use the scrollPosition to determine the month to be displayed above our dates.

  • We'll update our Calendar component and track the scroll position on the horizontal scrollview and update the scrollPosition state.
  • Then we'll use the scrollPosition and moment to generate the current month of the date in view.

PS: this onScroll functionality may not be the best implementation. However I challenge you to come up with a better one. I know there's one. If you do, don't hesitate to open a PR on the repo.

import { useState, useEffect } from 'react'import { StyleSheet, Text, View, ScrollView } from 'react-native'import moment from 'moment'import Date from './Date'const Calendar = ({ onSelectDate, selected }) => {  const [dates, setDates] = useState([])  const [scrollPosition, setScrollPosition] = useState(0)  const [currentMonth, setCurrentMonth] = useState()  // ... same as before  /**   * scrollPosition is the number of pixels the user has scrolled   * we divide it by 60 because each date is 80 pixels wide and we want to get the number of dates   * we add the number of dates to today to get the current month   * we format it as a string and set it as the currentMonth   */  const getCurrentMonth = () => {    const month = moment(dates[0]).add(scrollPosition / 60, 'days').format('MMMM')    setCurrentMonth(month)  }  useEffect(() => {    getCurrentMonth()  }, [scrollPosition])  return (    <>      <View style={styles.centered}>        <Text style={styles.title}>{currentMonth}</Text>      </View>      <View style={styles.dateSection}>        <View style={styles.scroll}>          <ScrollView            horizontal            showsHorizontalScrollIndicator={false}            // onScroll is a native event that returns the number of pixels the user has scrolled            scrollEventThrottle={16}            onScroll={(e) => setScrollPosition(e.nativeEvent.contentOffset.x)}          >            {dates.map((date, index) => (              <Date                key={index}                date={date}                onSelectDate={onSelectDate}                selected={selected}              />            ))}          </ScrollView>        </View>      </View>    </>  )}export default Calendarconst styles = StyleSheet.create({  centered: {    justifyContent: 'center',    alignItems: 'center',  },  title: {    fontSize: 20,    fontWeight: 'bold',  },  dateSection: {    width: '100%',    padding: 20,  },  scroll: {    height: 150,  },})

Now we can see the month and when you scroll to a new month, it should change the month displayed above.

finally

Fun exercise...

Try using the Animated API to center selected date or even make it feel like a wheel.

You can find all the code in this repo. PRs are welcome.


Original Link: https://dev.to/kharioki/horizontal-calendar-a-simple-date-picker-for-react-native-4h2

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To