Skip to content

Instantly share code, notes, and snippets.

@hmtri1011
Last active January 8, 2020 01:54
Show Gist options
  • Save hmtri1011/e10f21b023d0450015e4c3db1f5ee2bb to your computer and use it in GitHub Desktop.
Save hmtri1011/e10f21b023d0450015e4c3db1f5ee2bb to your computer and use it in GitHub Desktop.
Multiple range Calendar Component
import React, { PureComponent } from 'react'
import { View, StyleSheet, TouchableOpacity, Text } from 'react-native'
import { Calendar } from 'react-native-calendars'
import dateFnsFormat from 'date-fns/format'
import compareAsc from 'date-fns/compare_asc'
const theme = {
textSectionTitleColor: '#0D2421',
dayTextColor: '#0D2421',
arrowColor: '#065747',
monthTextColor: '#065747',
textMonthFontSize: 16,
textDayFontSize: 15,
textDayHeaderFontSize: 15
}
const availableDateRange = [
{
startDate: '2019-03-18',
endDate: '2019-04-01'
},
{
startDate: '2019-04-10',
endDate: '2019-05-01'
},
{
startDate: '2019-05-10',
endDate: '2019-06-02'
}
]
getDaysArray = (start, end) => {
for (var arr = [], dt = start; dt <= end; dt.setDate(dt.getDate() + 1)) {
arr.push(new Date(dt))
}
return arr.map(v => v.toISOString().slice(0, 10))
}
const availableDate = availableDateRange.reduce((acc, cur) => {
const dateList = getDaysArray(new Date(cur.startDate), new Date(cur.endDate))
return acc.concat(dateList)
}, [])
availableDate.sort((a, b) => compareAsc(new Date(a), new Date(b)))
//remove duplicate date if has
const uniqueAvailableDate = [...new Set(availableDate)]
const firstAvailableDate = uniqueAvailableDate[0]
const lastAvailableDate = uniqueAvailableDate[uniqueAvailableDate.length - 1]
const allDateList = getDaysArray(
new Date(firstAvailableDate),
new Date(lastAvailableDate)
)
const notAvailableDate = allDateList.filter(
date => !uniqueAvailableDate.includes(date)
)
const notAvailableDateStyle = notAvailableDate.reduce(
(acc, cur) => ({
...acc,
[cur]: {
selected: false,
selectedColor: '#9b9b9b',
disabled: true,
disableTouchEvent: true
}
}),
{}
)
class CustomCalendar extends PureComponent {
state = {
selectedDate: [],
clickedTime: 0
}
clickedDate = ''
handleSelectDate = date => {
const { dateString } = date
if (notAvailableDate.includes(dateString)) return
const { clickedTime, selectedDate } = this.state
const selectedDateFiltered = selectedDate.filter(dateRange => {
const existedDate = dateRange.find(dateInRange =>
[dateString, this.clickedDate].includes(dateInRange)
)
return existedDate ? false : true
})
if (clickedTime === 0) {
this.clickedDate = dateString
return this.setState(() => ({
clickedTime: 1
}))
}
if (clickedTime === 1) {
let startDate, endDate
if (compareAsc(new Date(this.clickedDate), new Date(dateString)) <= 0) {
startDate = this.clickedDate
endDate = dateString
} else {
startDate = dateString
endDate = this.clickedDate
}
const dateList = getDaysArray(new Date(startDate), new Date(endDate))
this.clickedDate = ''
return this.setState(() => {
//if [2019-03-14,2019-03-15, 2019-03-16] is on selectedDate
//and dateList is [2019-03-14, 2019-03-15, 2019-03-16, 2019-03-17]
//then [2019-03-14,2019-03-15, 2019-03-16] is filtered out
const selectedDateFilteredDateInCommon = selectedDateFiltered.filter(
dateRange => {
const shouldNotInclude = dateRange.every(dateInRange =>
dateList.includes(dateInRange)
)
return !shouldNotInclude
}
)
return {
clickedTime: 0,
selectedDate: [...selectedDateFilteredDateInCommon, [...dateList]]
}
})
}
}
render() {
const { selectedDate } = this.state
const selectedDateStyleArray = selectedDate.map(dayList => {
return dayList.reduce((acc, cur, curIndex) => {
return {
...acc,
[cur]: {
selected: true,
startingDay: curIndex === 0 ? true : false,
endingDay: curIndex === dayList.length - 1 ? true : false,
color: 'green'
}
}
}, {})
})
const selectedDateStyle = selectedDateStyleArray.reduce(
(acc, cur) => ({
...acc,
...cur
}),
{}
)
return (
<View style={styles.container}>
<Calendar
style={styles.calendarWrapper}
theme={theme}
onDayPress={this.handleSelectDate}
onDayLongPress={this.handleSelectDate}
minDate={dateFnsFormat(
new Date(uniqueAvailableDate[0]),
'YYYY-MM-DD'
)}
maxDate={dateFnsFormat(
new Date(uniqueAvailableDate[uniqueAvailableDate.length - 1]),
'YYYY-MM-DD'
)}
firstDay={1}
horizontal={true}
pagingEnabled={true}
onPressArrowLeft={substractMonth => substractMonth()}
onPressArrowRight={addMonth => addMonth()}
markedDates={{
[this.clickedDate]: {
selected: true,
startingDay: true,
endingDay: true,
color: 'green'
},
...selectedDateStyle,
...notAvailableDateStyle
}}
markingType={'period'}
/>
<View
style={{
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
borderRadius: 20,
marginTop: 10
}}
>
<TouchableOpacity
onPress={() =>
this.setState(() => ({
selectedDate: [],
clickedTime: 0
}))
}
>
<Text>Clear</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
height: 400,
width: '95%'
},
calendarWrapper: {
paddingVertical: 5
}
})
export default CustomCalendar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment