├── .gitignore
├── demo-datepicker.gif
├── index.js
├── SingleDatepicker
├── DayRow.js
├── Month.js
├── Day.js
└── index.js
├── RangeDatepicker
├── DayRow.js
├── DayHeader.js
├── Month.js
├── Day.js
└── index.js
├── package.json
├── helper
└── index.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 |
--------------------------------------------------------------------------------
/demo-datepicker.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apaajabolehd/react-native-range-datepicker/HEAD/demo-datepicker.gif
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import RangeDatepicker from './RangeDatepicker';
3 | import SingleDatepicker from './SingleDatepicker';
4 |
5 | export { SingleDatepicker };
6 | export default RangeDatepicker;
7 |
--------------------------------------------------------------------------------
/SingleDatepicker/DayRow.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React from 'react';
3 | import {
4 | View
5 | } from 'react-native';
6 | import Day from './Day';
7 |
8 | const DayRow = (props) => {
9 | return (
10 |
11 | {
12 | props.days.map((day, i) => {
13 | return (
14 |
15 | )
16 | })
17 | }
18 |
19 | );
20 | }
21 |
22 | export default DayRow;
--------------------------------------------------------------------------------
/RangeDatepicker/DayRow.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React from 'react';
3 | import {
4 | View
5 | } from 'react-native';
6 | import Day from './Day';
7 |
8 | const DayRow = (props) => {
9 | return (
10 |
11 | {
12 | props.days.map((day, i) => {
13 | return (
14 |
15 | )
16 | })
17 | }
18 |
19 | );
20 | }
21 |
22 | export default DayRow;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-range-datepicker",
3 | "version": "2.2.0",
4 | "description": "React native range datepicker (inspired by Airbnb Datepicker)",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "react-native",
11 | "datepicker",
12 | "range",
13 | "javascript"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/apaajabolehd/react-native-range-datepicker.git"
18 | },
19 | "author": "Hanson Citra ",
20 | "license": "ISC",
21 | "dependencies": {
22 | "dayjs": "^1.10.7",
23 | "prop-types": "^15.6.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/helper/index.js:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 | import customParseFormat from "dayjs/plugin/customParseFormat";
3 | import relativeTime from "dayjs/plugin/relativeTime";
4 | import advancedFormat from "dayjs/plugin/advancedFormat";
5 | import utc from "dayjs/plugin/utc";
6 | import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
7 | import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
8 | dayjs.extend(customParseFormat);
9 | dayjs.extend(relativeTime);
10 | dayjs.extend(advancedFormat);
11 | dayjs.extend(utc);
12 | dayjs.extend(isSameOrAfter);
13 | dayjs.extend(isSameOrBefore);
14 |
15 | const dayJsMod = (date, format, strict) => {
16 | if (!date || typeof date === "undefined") {
17 | return dayjs();
18 | } else {
19 | const stringifyDate = date.toString();
20 | if (!format || typeof format === "undefined") {
21 | return dayjs(stringifyDate);
22 | }
23 |
24 | if (typeof strict === "boolean") {
25 | return dayjs(stringifyDate, format, strict);
26 | } else {
27 | return dayjs(stringifyDate, format);
28 | }
29 | }
30 | };
31 |
32 | export { dayJsMod };
--------------------------------------------------------------------------------
/RangeDatepicker/DayHeader.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React from 'react';
3 | import {
4 | Dimensions,
5 | StyleSheet,
6 | Text,
7 | View
8 | } from 'react-native';
9 |
10 | const DEVICE_WIDTH = Dimensions.get('window').width;
11 |
12 | const DayHeader = (props) => {
13 | const {
14 | dayContainerOffset = 0,
15 | dayHeaderContainerStyle,
16 | dayHeaderStyle,
17 | dayHeadings
18 | } = props;
19 | return (
20 |
21 | {
22 | dayHeadings.map((day, i) => {
23 | return (
24 |
25 | {day}
26 |
27 | );
28 | })
29 | }
30 |
31 | );
32 | }
33 |
34 | const styles = StyleSheet.create({
35 | dayHeader : {
36 | flexDirection: 'row',
37 | borderBottomWidth: 1,
38 | paddingBottom: 10,
39 | paddingTop: 10,
40 | }
41 | });
42 |
43 | export default DayHeader;
--------------------------------------------------------------------------------
/SingleDatepicker/Month.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { memo } from 'react';
3 | import {
4 | View,
5 | Text
6 | } from 'react-native';
7 | import DayRow from './DayRow'
8 | import { dayJsMod } from '../helper';
9 |
10 | const areEqual = (prevProps, nextProps) => {
11 | if(nextProps.minDate != prevProps.minDate)
12 | return false;
13 |
14 | if(nextProps.maxDate != prevProps.maxDate)
15 | return false;
16 |
17 | if(nextProps.availableDates != prevProps.availableDates)
18 | return false;
19 |
20 | if(nextProps.startDate != prevProps.startDate)
21 | return false;
22 |
23 | if(nextProps.untilDate != prevProps.untilDate)
24 | return false;
25 |
26 | return true;
27 | }
28 |
29 | const Month = memo((props) => {
30 | const { month, dayProps } = props;
31 |
32 | const getDayStack = (month) => {
33 | let res = [];
34 | let currMonth = dayJsMod(month).month(); //get this month
35 | let currDate = dayJsMod(month).startOf("month"); //get first day in this month
36 |
37 | let dayColumn = [];
38 | let dayRow = [];
39 | let dayObject = {};
40 | let {availableDates, minDate, maxDate} = props;
41 |
42 | do{
43 | dayColumn = [];
44 | for(let i = 0; i < 7; i++){
45 | dayObject = {
46 | type : null,
47 | date: null
48 | };
49 | if(i == currDate.day() && currDate.month() == currMonth)
50 | {
51 | if(minDate && minDate.format("YYYYMMDD") && currDate.format("YYYYMMDD") < minDate.format("YYYYMMDD")){
52 | dayObject.type = 'disabled';
53 | }
54 | if(maxDate && maxDate.format("YYYYMMDD") && currDate.format("YYYYMMDD") > maxDate.format("YYYYMMDD")){
55 | dayObject.type = 'disabled';
56 | }
57 | if(availableDates && availableDates.indexOf(currDate.format("YYYYMMDD")) == -1){
58 | dayObject.type = 'blockout';
59 | }
60 |
61 | dayObject.date = currDate.clone().format('YYYYMMDD');
62 | dayColumn.push(dayObject);
63 | currDate = currDate.add(1, 'day');
64 | }
65 | else
66 | dayColumn.push(dayObject);
67 | }
68 |
69 | dayRow.push(dayColumn);
70 | } while (currDate.month() == currMonth);
71 |
72 | return dayRow;
73 | }
74 |
75 | const dayStack = getDayStack(dayJsMod(month, 'YYYYMM'));
76 |
77 | return (
78 |
79 | {dayJsMod(month, 'YYYYMM').format("MMMM YYYY")}
80 |
81 | {
82 | dayStack.map((days, i) => {
83 | return (
84 |
85 | )
86 | })
87 | }
88 |
89 |
90 | );
91 | }, areEqual);
92 |
93 | export default Month;
--------------------------------------------------------------------------------
/SingleDatepicker/Day.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { memo } from 'react';
3 | import {
4 | View,
5 | Text,
6 | TouchableWithoutFeedback,
7 | Dimensions
8 | } from 'react-native';
9 | import { dayJsMod } from '../helper';
10 |
11 |
12 | const DEVICE_WIDTH = Dimensions.get('window').width;
13 |
14 | const areEqual = (prevProps, nextProps) => {
15 | return true;
16 | if(nextProps.day.type != prevProps.day.type){
17 | return false;
18 | }
19 |
20 | return true;
21 | };
22 |
23 | const Day = memo((props) => {
24 | let {day, dayProps} = props;
25 | let dayStyle = {backgroundColor : 'transparent', position: 'relative', width: "14.28%"};
26 | let textDayStyle = {color: 'black'};
27 |
28 | switch(day.type){
29 | case "disabled" :
30 | case "blockout" :
31 | textDayStyle = {color: '#ccc'};
32 | default: break;
33 | }
34 |
35 | if(day.date){
36 | if(day.type == 'disabled')
37 | return (
38 |
39 |
40 | {dayJsMod(day.date, 'YYYYMMDD').date()}
41 | {day.date == dayJsMod().format("YYYYMMDD") ? (__) : null}
42 |
43 |
44 | );
45 | else if(day.type == 'blockout') {
46 | const strikeTop = Math.floor(DEVICE_WIDTH / -22);
47 | return (
48 |
49 |
50 | {dayJsMod(day.date, 'YYYYMMDD').date()}
51 | __
52 |
53 |
54 | );
55 | }
56 | else
57 | return (
58 | props.onSelectDate(dayJsMod(day.date, 'YYYYMMDD'))}>
59 |
60 | {dayJsMod(day.date, 'YYYYMMDD').date()}
61 | {day.date == dayJsMod().format("YYYYMMDD") ? (__) : null}
62 |
63 |
64 | );
65 | }
66 | else
67 | return (
68 |
69 |
70 | {null}
71 |
72 |
73 | );
74 | }, areEqual)
75 |
76 | export default Day;
--------------------------------------------------------------------------------
/SingleDatepicker/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { useEffect, useState } from 'react';
3 | import PropTypes from 'prop-types';
4 | import {
5 | Text,
6 | View,
7 | FlatList,
8 | StyleSheet,
9 | } from 'react-native';
10 | import Month from './Month';
11 | // import styles from './styles';
12 | import { dayJsMod } from '../helper';
13 |
14 | const SingleDatepicker = (props) => {
15 | const [availableDates, setAvailableDates] = useState(props.availableDates || null)
16 |
17 | useEffect(() => {
18 | setAvailableDates(props.availableDates);
19 | }, [props.availableDates])
20 |
21 |
22 | const onSelectDate = (date) => {
23 | props.onSelect(date);
24 | props.onClose();
25 | }
26 |
27 | const getMonthStack = () => {
28 | let res = [];
29 | const { maxMonth, initialMonth } = props;
30 | let initMonth = dayJsMod();
31 | if(initialMonth && initialMonth != '')
32 | initMonth = dayJsMod(initialMonth, 'YYYYMM');
33 |
34 | for(let i = 0; i < maxMonth; i++){
35 | res.push(initMonth.clone().add(i, 'month').format('YYYYMM'));
36 | }
37 |
38 | return res;
39 | }
40 |
41 | const handleRenderRow = (month, index) => {
42 | const { selectedBackgroundColor, selectedTextColor, todayColor, ignoreMinDate, minDate, maxDate } = props;
43 |
44 | if(availableDates && availableDates.length > 0){
45 | availableDates = availableDates.filter(function(d){
46 | if(d.indexOf(month) >= 0)
47 | return true;
48 | });
49 | }
50 |
51 | return (
52 |
60 | )
61 | }
62 |
63 | return (
64 |
65 | {
66 | props.showClose ?
67 | (
68 | {
69 | props.showClose && Close
70 | }
71 | )
72 | :
73 | null
74 | }
75 | {
76 | props.infoText != "" &&
77 |
78 | {props.infoText}
79 |
80 | }
81 |
82 | {
83 | props.dayHeadings.map((day, i) => {
84 | return ({day})
85 | })
86 | }
87 |
88 | {
91 | return handleRenderRow(item, index)
92 | }}
93 | keyExtractor = { (item, index) => index.toString() }
94 | showsVerticalScrollIndicator={false}
95 | />
96 |
97 | )
98 | }
99 |
100 | SingleDatepicker.defaultProps = {
101 | initialMonth: '',
102 | dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
103 | maxMonth: 12,
104 | showClose: true,
105 | onClose: () => {},
106 | onSelect: () => {},
107 | selectedBackgroundColor: 'green',
108 | selectedTextColor: 'white',
109 | todayColor: 'green',
110 | minDate: '',
111 | maxDate: '',
112 | infoText: '',
113 | infoStyle: {color: '#fff', fontSize: 13},
114 | infoContainerStyle: {marginRight: 20, paddingHorizontal: 20, paddingVertical: 5, backgroundColor: 'green', borderRadius: 20, alignSelf: 'flex-end'},
115 | };
116 |
117 |
118 | SingleDatepicker.propTypes = {
119 | initialMonth: PropTypes.string,
120 | dayHeadings: PropTypes.arrayOf(PropTypes.string),
121 | availableDates: PropTypes.arrayOf(PropTypes.string),
122 | maxMonth: PropTypes.number,
123 | minDate: PropTypes.string,
124 | maxDate: PropTypes.string,
125 | showClose: PropTypes.bool,
126 | onClose: PropTypes.func,
127 | onSelect: PropTypes.func,
128 | selectedBackgroundColor: PropTypes.string,
129 | selectedTextColor: PropTypes.string,
130 | todayColor: PropTypes.string,
131 | infoText: PropTypes.string,
132 | infoStyle: PropTypes.object,
133 | infoContainerStyle: PropTypes.object,
134 | }
135 |
136 | const styles = StyleSheet.create({
137 | dayHeader : {
138 | flexDirection: 'row',
139 | borderBottomWidth: 1,
140 | paddingBottom: 10,
141 | paddingTop: 10,
142 | },
143 | buttonWrapper : {
144 | paddingVertical: 10,
145 | paddingHorizontal: 15,
146 | backgroundColor: 'white',
147 | borderTopWidth: 1,
148 | borderColor: '#ccc',
149 | alignItems: 'stretch'
150 | },
151 | });
152 |
153 | export default SingleDatepicker;
--------------------------------------------------------------------------------
/RangeDatepicker/Month.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { memo } from 'react';
3 | import PropTypes from 'prop-types';
4 | import {
5 | View,
6 | Text
7 | } from 'react-native';
8 | import DayRow from './DayRow';
9 | import DayHeader from './DayHeader';
10 | import { dayJsMod } from '../helper'
11 |
12 | function capitalize(str){
13 | return str.charAt(0).toUpperCase() + str.slice(1);
14 | }
15 |
16 | const areEqual = (prevProps, nextProps) => {
17 | if(nextProps.minDate != prevProps.minDate)
18 | return false;
19 |
20 | if(nextProps.maxDate != prevProps.maxDate)
21 | return false;
22 |
23 | if(nextProps.availableDates != prevProps.availableDates)
24 | return false;
25 |
26 | if(nextProps.startDate != prevProps.startDate)
27 | return false;
28 |
29 | if(nextProps.untilDate != prevProps.untilDate)
30 | return false;
31 |
32 | return true;
33 | }
34 |
35 | const Month = memo((props) => {
36 | const { month, dayProps, titleStyle, titleFormat, capitalizeTitle, dayHeaderProps } = props;
37 |
38 | const getDayStack = (month) => {
39 | let res = [];
40 | let currMonth = dayJsMod(month).month(); //get this month
41 | let currDate = dayJsMod(month).startOf("month"); //get first day in this month
42 |
43 | let dayColumn = [];
44 | let dayRow = [];
45 | let dayObject = {};
46 | let {startDate, untilDate, availableDates, minDate, maxDate, ignoreMinDate} = props;
47 |
48 | do{
49 | dayColumn = [];
50 | for(let i = 0; i < 7; i++){
51 | dayObject = {
52 | type : null,
53 | date: null
54 | };
55 | if(i == currDate.day() && currDate.month() == currMonth)
56 | {
57 | if(minDate && minDate.format("YYYYMMDD") && currDate.format("YYYYMMDD") < minDate.format("YYYYMMDD")){
58 | if(startDate && startDate.format('YYYYMMDD') > currDate.format("YYYYMMDD") && currDate.format("YYYYMMDD") > dayJsMod().format("YYYYMMDD") && ignoreMinDate){}
59 | else{
60 | dayObject.type = 'disabled';
61 | }
62 | }
63 | if(maxDate && maxDate.format("YYYYMMDD") && currDate.format("YYYYMMDD") > maxDate.format("YYYYMMDD")){
64 | dayObject.type = 'disabled';
65 | }
66 | if(availableDates && availableDates.indexOf(currDate.format("YYYYMMDD")) == -1){
67 | dayObject.type = 'blockout';
68 | }
69 | if(startDate && startDate.format('YYYYMMDD') == currDate.format('YYYYMMDD')){
70 | if(!untilDate)
71 | dayObject.type = 'single';
72 | else{
73 | dayObject.type = 'first';
74 | }
75 | }
76 | if(untilDate && untilDate.format('YYYYMMDD') == currDate.format('YYYYMMDD')){
77 | dayObject.type = 'last';
78 | }
79 | if((startDate && startDate.format('YYYYMMDD') < currDate.format('YYYYMMDD')) &&
80 | (untilDate && untilDate.format('YYYYMMDD') > currDate.format('YYYYMMDD')))
81 | dayObject.type = 'between';
82 |
83 | dayObject.date = currDate.clone().format('YYYYMMDD');
84 | dayColumn.push(dayObject);
85 | currDate = currDate.add(1, 'day');
86 | }
87 | else{
88 | if(startDate && untilDate &&
89 | (
90 | startDate.format('YYYYMMDD') < currDate.format('YYYYMMDD') &&
91 | untilDate.format('YYYYMMDD') >= currDate.format('YYYYMMDD')
92 | )
93 | )
94 | dayObject.type = 'between';
95 |
96 | dayColumn.push(dayObject);
97 | }
98 | }
99 |
100 | dayRow.push(dayColumn);
101 | } while (currDate.month() == currMonth);
102 |
103 | return dayRow;
104 | }
105 |
106 | const dayStack = getDayStack(dayJsMod(month, 'YYYYMM'));
107 |
108 | return (
109 |
110 |
111 | {capitalizeTitle ?
112 | capitalize(dayJsMod(month, 'YYYYMM').format(titleFormat)) :
113 | dayJsMod(month, 'YYYYMM').format(titleFormat)
114 | }
115 |
116 | {props.showDaysHeader && (
117 |
118 | )}
119 |
120 | {
121 | dayStack.map((days, i) => {
122 | return (
123 |
124 | )
125 | })
126 | }
127 |
128 |
129 | );
130 | }, areEqual);
131 |
132 | Month.defaultProps = {
133 | titleFormat: 'MMMM YYYY',
134 | titleStyle: { fontSize: 20, padding: 20 },
135 | dayHeaderProps: {},
136 | showDaysHeader: false,
137 | capitalizeTitle: false,
138 | };
139 |
140 | Month.propTypes = {
141 | titleFormat: PropTypes.string,
142 | titleStyle: PropTypes.object,
143 | dayHeaderProps: PropTypes.object,
144 | showDaysHeader: PropTypes.bool,
145 | capitalizeTitle: PropTypes.bool,
146 | };
147 |
148 | export default Month;
--------------------------------------------------------------------------------
/RangeDatepicker/Day.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { memo } from 'react';
3 | import {
4 | View,
5 | Text,
6 | TouchableWithoutFeedback,
7 | Dimensions,
8 | StyleSheet
9 | } from 'react-native';
10 | import { dayJsMod } from '../helper';
11 |
12 | const DEVICE_WIDTH = Dimensions.get('window').width;
13 |
14 | const areEqual = (prevProps, nextProps) => {
15 | if(nextProps.day.type != prevProps.day.type){
16 | return false;
17 | }
18 |
19 | if(nextProps.onSelectDate != prevProps.onSelectDate){
20 | return false;
21 | }
22 |
23 | return true;
24 | };
25 |
26 | const Day = memo((props) => {
27 | const {
28 | day,
29 | dayProps,
30 | } = props;
31 |
32 | const {
33 | dayBackgroundColor,
34 | dayTextColor,
35 | pointBackgroundColor,
36 | pointTextColor,
37 | selectedBackgroundColor,
38 | selectedTextColor,
39 | dayContainerOffset = 10,
40 | } = dayProps;
41 |
42 | const side = Math.floor(DEVICE_WIDTH / 7)+2;
43 | const size = {
44 | width: side,
45 | height: side,
46 | flexDirection: 'row',
47 | justifyContent: 'center',
48 | alignItems: 'center',
49 | };
50 |
51 | const dayWrapperStyle = {
52 | ...size,
53 | backgroundColor : 'transparent',
54 | };
55 |
56 | const dayStyle = {
57 | width: side - dayContainerOffset,
58 | height: side - dayContainerOffset,
59 | // flex: 0,
60 | backgroundColor : dayBackgroundColor || 'transparent',
61 | position: 'relative',
62 | borderRadius: side - dayContainerOffset,
63 | };
64 |
65 | const textDayStyle = { color: dayTextColor || 'black' };
66 |
67 |
68 | switch(day.type) {
69 | case "single" :
70 | dayStyle.backgroundColor = pointBackgroundColor || selectedBackgroundColor;
71 | textDayStyle.color = pointTextColor || selectedTextColor;
72 | break;
73 | case "first" :
74 | dayStyle.backgroundColor = pointBackgroundColor || selectedBackgroundColor;
75 | textDayStyle.color = pointTextColor || selectedTextColor;
76 | dayWrapperStyle.backgroundColor = selectedBackgroundColor;
77 | dayWrapperStyle.borderBottomLeftRadius = side;
78 | dayWrapperStyle.borderTopLeftRadius = side;
79 | break;
80 | case "last" :
81 | dayStyle.backgroundColor = pointBackgroundColor || selectedBackgroundColor;
82 | textDayStyle.color = pointTextColor || selectedTextColor;
83 | dayWrapperStyle.backgroundColor = selectedBackgroundColor;
84 | dayWrapperStyle.borderBottomRightRadius = side;
85 | dayWrapperStyle.borderTopRightRadius = side;
86 | break;
87 | case "between" :
88 | dayStyle.backgroundColor = selectedBackgroundColor;
89 | textDayStyle.color = selectedTextColor;
90 | dayWrapperStyle.backgroundColor = selectedBackgroundColor;
91 | break;
92 | case "disabled" :
93 | case "blockout" :
94 | textDayStyle.color = '#ccc';
95 | default: break;
96 | }
97 |
98 | if(day.date){
99 | if(day.type == 'disabled')
100 | return (
101 |
102 |
103 |
104 | {dayJsMod(day.date, 'YYYYMMDD').date()}
105 | {day.date == dayJsMod().format("YYYYMMDD") ? (__) : null}
106 |
107 |
108 |
109 | );
110 | else if(day.type == 'blockout') {
111 | const strikeTop = Math.floor(DEVICE_WIDTH / -22);
112 | return (
113 |
114 |
115 |
116 | {dayJsMod(day.date, 'YYYYMMDD').date()}
117 |
118 | __
119 |
120 |
121 |
122 |
123 | );
124 | }
125 | else
126 | return (
127 | props.onSelectDate(dayJsMod(day.date, 'YYYYMMDD'))}>
128 |
129 |
130 |
131 | {dayJsMod(day.date, 'YYYYMMDD').date()}
132 |
133 | {day.date == dayJsMod().format("YYYYMMDD") ?
134 | (
135 | __
136 | )
137 | : null}
138 |
139 |
140 |
141 | );
142 | }
143 | else
144 | return (
145 |
146 |
147 |
148 | {null}
149 |
150 |
151 |
152 | );
153 | }, areEqual)
154 |
155 | export default Day;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-range-datepicker
2 |
3 | This is my first npm package, inspired by Airbnb datepicker.
4 |
5 | 
6 |
7 | ### Install
8 | ```sh
9 | $ npm i react-native-range-datepicker --save
10 | ```
11 |
12 | ### How to use
13 | ```jsx
14 | import DatepickerRange from 'react-native-range-datepicker';
15 |
16 | this.setState({ startDate, untilDate })}
20 | />
21 | ```
22 |
23 |
24 | ### Default props RangeDatepicker
25 | ```jsx
26 | static defaultProps = {
27 | monthProps: {
28 | titleFormat: 'MMMM YYYY',
29 | titleStyle: { fontSize: 20, padding: 20 },
30 | dayHeaderProps: {},
31 | showDaysHeader: false,
32 | capitalizeTitle: false,
33 | },
34 | buttonText: 'Select Date',
35 | flatListProps: {},
36 | closeButtonText: 'Close',
37 | chosenDateTextColor: '#666',
38 | monthProps: {},
39 | dayHeaderDividerColor: "#000000",
40 | initialMonth: '',
41 | dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
42 | dayHeaderStyle: {},
43 | dayHeaderContainerStyle: {},
44 | maxMonth: 12,
45 | buttonColor: 'green',
46 | buttonContainerStyle: {},
47 | flatListProps: {},
48 | monthProps: {},
49 | showReset: true,
50 | showClose: true,
51 | showConfirmButton: true,
52 | showSelectedRange: true,
53 | showsVerticalScrollIndicator: false,
54 | showDaysHeader: true,
55 | ignoreMinDate: false,
56 | isHistorical: false,
57 | dayContainerOffset: 0,
58 | onClose: () => {},
59 | onSelect: () => {},
60 | onConfirm: () => {},
61 | placeHolderStart: 'Start Date',
62 | placeHolderUntil: 'Until Date',
63 | selectedBackgroundColor: 'green',
64 | selectedTextColor: 'white',
65 | dayBackgroundColor: '',
66 | dayTextColor: '',
67 | pointBackgroundColor: '',
68 | pointTextColor: '',
69 | todayColor: 'green',
70 | textColor: '#000000',
71 | resetButtonText: "Reset",
72 | startDate: '',
73 | untilDate: '',
74 | minDate: '',
75 | maxDate: '',
76 | infoText: '',
77 | showSelectionInfo: true,
78 | showButton: true,
79 | infoStyle: {color: '#fff', fontSize: 13},
80 | infoContainerStyle: {marginRight: 20, paddingHorizontal: 20, paddingVertical: 5, backgroundColor: 'green', borderRadius: 20, alignSelf: 'flex-end'}
81 | };
82 | ```
83 |
84 | ### Proptypes RangeDatepicker
85 | ```jsx
86 | static propTypes = {
87 | monthProps: PropTypes.shape({
88 | titleFormat: PropTypes.string,
89 | titleStyle: PropTypes.object,
90 | }),
91 | initialMonth: PropTypes.string,
92 | dayHeadings: PropTypes.arrayOf(PropTypes.string),
93 | availableDates: PropTypes.arrayOf(PropTypes.string),
94 | maxMonth: PropTypes.number,
95 | buttonColor: PropTypes.string,
96 | buttonText: PropTypes.string,
97 | dayHeaderDividerColor: PropTypes.string,
98 | closeButtonText: PropTypes.string,
99 | chosenDateTextColor: PropTypes.string,
100 | flatListProps: PropTypes.object,
101 | dayHeaderStyle: PropTypes.object,
102 | dayHeaderContainerStyle: PropTypes.object,
103 | buttonContainerStyle: PropTypes.object,
104 | monthProps: PropTypes.object,
105 | startDate: PropTypes.string,
106 | untilDate: PropTypes.string,
107 | minDate: PropTypes.string,
108 | maxDate: PropTypes.string,
109 | showReset: PropTypes.bool,
110 | showClose: PropTypes.bool,
111 | dayContainerOffset: PropTypes.number,
112 | showConfirmButton: PropTypes.bool,
113 | showSelectedRange: PropTypes.bool,
114 | showsVerticalScrollIndicator: PropTypes.bool,
115 | ignoreMinDate: PropTypes.bool,
116 | isHistorical: PropTypes.bool,
117 | onClose: PropTypes.func,
118 | onSelect: PropTypes.func,
119 | onConfirm: PropTypes.func,
120 | placeHolderStart: PropTypes.string,
121 | placeHolderUntil: PropTypes.string,
122 | selectedBackgroundColor: PropTypes.string,
123 | resetButtonText: PropTypes.string,
124 | selectedTextColor: PropTypes.string,
125 | dayBackgroundColor: PropTypes.string,
126 | dayTextColor: PropTypes.string,
127 | pointBackgroundColor: PropTypes.string,
128 | pointTextColor: PropTypes.string,
129 | todayColor: PropTypes.string,
130 | infoText: PropTypes.string,
131 | infoStyle: PropTypes.object,
132 | showSelectionInfo: PropTypes.bool,
133 | showButton: PropTypes.bool,
134 | infoContainerStyle: PropTypes.object
135 | }
136 |
137 | ```
138 |
139 |
140 |
141 | ```jsx
142 | import {SingleDatepicker} from 'react-native-range-datepicker';
143 |
144 | this.setState({ date })}
146 | />
147 | ```
148 |
149 | ### How to set fist day of week
150 | ```js
151 |
152 | import 'moment/locale/en-gb';
153 | moment.locale("en-gb");
154 |
155 | ```
156 |
157 | ### How to set days heades
158 | ```js
159 |
160 | const dayHeadings =
161 | [...Array(7).keys()]
162 | .map(day => moment().weekday(day).format('dd'))
163 | .map(_capitalize);
164 |
165 | ```
166 |
167 | ### Default props SingleDatepicker
168 | ```jsx
169 | static defaultProps = {
170 | initialMonth: '',
171 | dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
172 | maxMonth: 12,
173 | showClose: true,
174 | onClose: () => {},
175 | onSelect: () => {},
176 | selectedBackgroundColor: 'green',
177 | selectedTextColor: 'white',
178 | todayColor: 'green',
179 | minDate: '',
180 | maxDate: '',
181 | infoText: '',
182 | infoStyle: {color: '#fff', fontSize: 13},
183 | infoContainerStyle: {marginRight: 20, paddingHorizontal: 20, paddingVertical: 5, backgroundColor: 'green', borderRadius: 20, alignSelf: 'flex-end'},
184 | };
185 | ```
186 |
187 | ### Proptypes SingleDatepicker
188 | ```jsx
189 | static propTypes = {
190 | initialMonth: PropTypes.string,
191 | dayHeadings: PropTypes.arrayOf(React.PropTypes.string),
192 | availableDates: PropTypes.arrayOf(React.PropTypes.string),
193 | maxMonth: PropTypes.number,
194 | minDate: PropTypes.string,
195 | maxDate: PropTypes.string,
196 | showClose: PropTypes.bool,
197 | onClose: PropTypes.func,
198 | onSelect: PropTypes.func,
199 | selectedBackgroundColor: PropTypes.string,
200 | selectedTextColor: PropTypes.string,
201 | todayColor: PropTypes.string,
202 | infoText: PropTypes.string,
203 | infoStyle: PropTypes.object,
204 | infoContainerStyle: PropTypes.object,
205 | }
206 | ```
207 |
208 | ### Update 23/07/2019
209 | * RN 0.6 compatibility: replaced ListView by FlatList
210 | * added more flexible width based on actual container size
211 |
--------------------------------------------------------------------------------
/RangeDatepicker/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, { useEffect, useState } from 'react';
3 | import PropTypes from 'prop-types';
4 | import {
5 | Text,
6 | View,
7 | FlatList,
8 | StyleSheet,
9 | Button,
10 | } from 'react-native';
11 | import Month from './Month';
12 | // import styles from './styles';
13 | import { dayJsMod } from '../helper';
14 |
15 | const RangeDatepicker = (props) => {
16 | const [startDate, setStartDate] = useState(props.startDate && dayJsMod(props.startDate, 'YYYYMMDD'));
17 | const [untilDate, setUntilDate] = useState(props.untilDate && dayJsMod(props.untilDate, 'YYYYMMDD'));
18 | const [availableDates, setAvailableDates] = useState(props.availableDates || null);
19 |
20 | useEffect(() => {
21 | setAvailableDates(props.availableDates)
22 | }, [props.availableDates])
23 |
24 | const onSelectDate = (date) => {
25 | let tempStartDate = null;
26 | let tempUntilDate = null;
27 | // console.log('startDate', startDate);
28 | // console.log('untilDate', untilDate);
29 |
30 | if(startDate && !untilDate)
31 | {
32 | if(date.format('YYYYMMDD') < startDate.format('YYYYMMDD') || isInvalidRange(date)){
33 | // console.log(1);
34 | tempStartDate = date;
35 | }
36 | else if(date.format('YYYYMMDD') > startDate.format('YYYYMMDD')){
37 | // console.log(2);
38 | tempStartDate = startDate;
39 | tempUntilDate = date;
40 | }
41 | else{
42 | // console.log(3);
43 | tempStartDate = null;
44 | tempUntilDate = null;
45 | }
46 | }
47 | else if(!isInvalidRange(date)) {
48 | // console.log(4);
49 | tempStartDate = date;
50 | }
51 | else {
52 | // console.log(5);
53 | tempStartDate = null;
54 | tempUntilDate = null;
55 | }
56 |
57 | setStartDate(tempStartDate);
58 | setUntilDate(tempUntilDate);
59 | props.onSelect(tempStartDate, tempUntilDate);
60 | }
61 |
62 | const isInvalidRange = (date) => {
63 | if(availableDates && availableDates.length > 0){
64 | //select endDate condition
65 | if(startDate && !untilDate) {
66 | for(let i = startDate.format('YYYYMMDD'); i <= date.format('YYYYMMDD'); i = dayJsMod(i, 'YYYYMMDD').add(1, 'days').format('YYYYMMDD')){
67 | if(availableDates.indexOf(i) == -1 && startDate.format('YYYYMMDD') != i)
68 | return true;
69 | }
70 | }
71 | //select startDate condition
72 | else if(availableDates.indexOf(date.format('YYYYMMDD')) == -1){
73 | return true;
74 | }
75 | }
76 |
77 | return false;
78 | }
79 |
80 | const getMonthStack = () => {
81 | let res = [];
82 | const { maxMonth, initialMonth, isHistorical } = props;
83 | let initMonth = dayJsMod();
84 | if(initialMonth && initialMonth != '')
85 | initMonth = dayJsMod(initialMonth, 'YYYYMM');
86 |
87 | for(let i = 0; i < maxMonth; i++){
88 | res.push(
89 | !isHistorical ? (
90 | initMonth.clone().add(i, 'month').format('YYYYMM')
91 | ) : (
92 | initMonth.clone().subtract(i, 'month').format('YYYYMM')
93 | )
94 | );
95 | }
96 |
97 | return res;
98 | }
99 |
100 | const onReset = () => {
101 | setStartDate(null);
102 | setUntilDate(null);
103 |
104 | props.onSelect(null, null);
105 | }
106 |
107 | const handleConfirmDate = () => {
108 | props.onConfirm && props.onConfirm(startDate,untilDate);
109 | }
110 |
111 | const handleRenderRow = (month, index) => {
112 | const { selectedBackgroundColor, selectedTextColor, todayColor, ignoreMinDate, minDate, maxDate } = props;
113 | if(availableDates && availableDates.length > 0){
114 | availableDates = availableDates.filter(function(d){
115 | if(d.indexOf(month) >= 0)
116 | return true;
117 | });
118 | }
119 |
120 | return (
121 |
131 | )
132 | }
133 |
134 |
135 | return (
136 |
137 | {
138 | props.showClose || props.showReset ?
139 | (
140 | {
141 | props.showClose && Close
142 | }
143 | {
144 | props.showReset && Reset
145 | }
146 | )
147 | :
148 | null
149 | }
150 | {
151 | props.showSelectionInfo ?
152 | (
153 |
154 |
155 |
156 | { startDate ? dayJsMod(startDate).format("MMM DD YYYY") : props.placeHolderStart}
157 |
158 |
159 |
160 |
161 |
162 | /
163 |
164 |
165 |
166 |
167 |
168 | { untilDate ? dayJsMod(untilDate).format("MMM DD YYYY") : props.placeHolderUntil}
169 |
170 |
171 |
172 | ) : null
173 | }
174 |
175 | {
176 | props.infoText != "" &&
177 |
178 | {props.infoText}
179 |
180 | }
181 |
182 | {
183 | props.dayHeadings.map((day, i) => {
184 | return ({day})
185 | })
186 | }
187 |
188 | {
192 | return handleRenderRow(item, index)
193 | }}
194 | keyExtractor = { (item, index) => index.toString() }
195 | showsVerticalScrollIndicator={false}
196 | />
197 |
198 | {
199 | props.showButton ?
200 | (
201 |
202 |
206 |
207 | ) : null
208 | }
209 |
210 | )
211 | }
212 |
213 | RangeDatepicker.propTypes = {
214 | initialMonth: PropTypes.string,
215 | dayHeadings: PropTypes.arrayOf(PropTypes.string),
216 | availableDates: PropTypes.arrayOf(PropTypes.string),
217 | maxMonth: PropTypes.number,
218 | buttonColor: PropTypes.string,
219 | buttonContainerStyle: PropTypes.object,
220 | startDate: PropTypes.string,
221 | untilDate: PropTypes.string,
222 | minDate: PropTypes.string,
223 | maxDate: PropTypes.string,
224 | showReset: PropTypes.bool,
225 | showClose: PropTypes.bool,
226 | ignoreMinDate: PropTypes.bool,
227 | isHistorical: PropTypes.bool,
228 | onClose: PropTypes.func,
229 | onSelect: PropTypes.func,
230 | onConfirm: PropTypes.func,
231 | placeHolderStart: PropTypes.string,
232 | placeHolderUntil: PropTypes.string,
233 | selectedBackgroundColor: PropTypes.string,
234 | selectedTextColor: PropTypes.string,
235 | todayColor: PropTypes.string,
236 | infoText: PropTypes.string,
237 | infoStyle: PropTypes.object,
238 | infoContainerStyle: PropTypes.object,
239 | showSelectionInfo: PropTypes.bool,
240 | showButton: PropTypes.bool,
241 | };
242 |
243 | RangeDatepicker.defaultProps = {
244 | initialMonth: '',
245 | dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
246 | maxMonth: 12,
247 | buttonColor: 'green',
248 | buttonContainerStyle: {},
249 | showReset: true,
250 | showClose: true,
251 | ignoreMinDate: false,
252 | isHistorical: false,
253 | onClose: () => {},
254 | onSelect: () => {},
255 | onConfirm: () => {},
256 | placeHolderStart: 'Start Date',
257 | placeHolderUntil: 'Until Date',
258 | selectedBackgroundColor: 'green',
259 | selectedTextColor: 'white',
260 | todayColor: 'green',
261 | startDate: '',
262 | untilDate: '',
263 | minDate: '',
264 | maxDate: '',
265 | infoText: '',
266 | infoStyle: {color: '#fff', fontSize: 13},
267 | infoContainerStyle: {marginRight: 20, paddingHorizontal: 20, paddingVertical: 5, backgroundColor: 'green', borderRadius: 20, alignSelf: 'flex-end'},
268 | showSelectionInfo: true,
269 | showButton: true,
270 | };
271 |
272 | const styles = StyleSheet.create({
273 | dayHeader : {
274 | flexDirection: 'row',
275 | borderBottomWidth: 1,
276 | paddingBottom: 10,
277 | paddingTop: 10,
278 | },
279 | buttonWrapper : {
280 | paddingVertical: 10,
281 | paddingHorizontal: 15,
282 | backgroundColor: 'white',
283 | borderTopWidth: 1,
284 | borderColor: '#ccc',
285 | alignItems: 'stretch'
286 | },
287 | });
288 |
289 | export default RangeDatepicker;
--------------------------------------------------------------------------------