├── .eslintrc
├── extras
├── logo.png
├── datetimepicker-ios.gif
└── datetimepicker-android.gif
├── src
├── index.js
├── CustomModal
│ ├── index.style.js
│ └── index.js
├── CustomDatePickerIOS
│ ├── index.style.js
│ └── index.js
└── CustomDatePickerAndroid
│ └── index.js
├── .gitignore
├── .npmignore
├── package.json
└── README.md
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-mostaza-react",
3 | "parser": "babel-eslint"
4 | }
5 |
--------------------------------------------------------------------------------
/extras/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-modal-datetime-picker/master/extras/logo.png
--------------------------------------------------------------------------------
/extras/datetimepicker-ios.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-modal-datetime-picker/master/extras/datetimepicker-ios.gif
--------------------------------------------------------------------------------
/extras/datetimepicker-android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corymsmith/react-native-modal-datetime-picker/master/extras/datetimepicker-android.gif
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 | import CustomDatePickerAndroid from './CustomDatePickerAndroid'
3 | import CustomDatePickerIOS from './CustomDatePickerIOS'
4 |
5 | const IS_ANDROID = Platform.OS === 'android'
6 |
7 | export default IS_ANDROID ? CustomDatePickerAndroid : CustomDatePickerIOS
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | *.iml
28 | .idea
29 | .gradle
30 | local.properties
31 |
32 | # node.js
33 | #
34 | node_modules/
35 | npm-debug.log
36 |
37 | # BUCK
38 | buck-out/
39 | \.buckd/
40 | android/app/libs
41 | android/keystores/debug.keystore
42 |
--------------------------------------------------------------------------------
/src/CustomModal/index.style.js:
--------------------------------------------------------------------------------
1 | import { Dimensions, StyleSheet } from 'react-native'
2 |
3 | const DEVICE_WIDTH = Dimensions.get('window').width
4 | const DEVICE_HEIGHT = Dimensions.get('window').height
5 |
6 | export default StyleSheet.create({
7 | backdrop: {
8 | position: 'absolute',
9 | top: 0,
10 | bottom: 0,
11 | left: 0,
12 | right: 0,
13 | height: DEVICE_HEIGHT,
14 | width: DEVICE_WIDTH,
15 | opacity: 0,
16 | backgroundColor: 'black'
17 | },
18 | contentContainer: {
19 | flex: 1,
20 | justifyContent: 'center',
21 | margin: DEVICE_WIDTH * 0.05
22 | }
23 | })
24 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | *.iml
28 | .idea
29 | .gradle
30 | local.properties
31 |
32 | # node.js
33 | #
34 | node_modules/
35 | npm-debug.log
36 |
37 | # BUCK
38 | buck-out/
39 | \.buckd/
40 | android/app/libs
41 | android/keystores/debug.keystore
42 |
43 | # Images and extras
44 | extras/
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-modal-datetime-picker",
3 | "version": "1.0.2",
4 | "description": "A react-native datetime-picker for Android and iOS",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "lint": "eslint src",
8 | "test": "npm run lint"
9 | },
10 | "keywords": [
11 | "react-native",
12 | "react",
13 | "native",
14 | "date",
15 | "time",
16 | "picker",
17 | "android",
18 | "ios"
19 | ],
20 | "author": "Mazzarolo Matteo",
21 | "license": "ISC",
22 | "dependencies": {
23 | "moment": "^2.15.0",
24 | "react-native-animatable": "^0.6.1"
25 | },
26 | "devDependencies": {
27 | "babel-eslint": "^6.1.2",
28 | "eslint": "^3.5.0",
29 | "eslint-config-mostaza-react": "^1.11.2",
30 | "eslint-plugin-import": "^1.15.0",
31 | "eslint-plugin-jsx-a11y": "^2.2.2",
32 | "eslint-plugin-react": "^6.2.1",
33 | "react": ">=15.0.0",
34 | "react-native": ">=0.24.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/CustomDatePickerIOS/index.style.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native'
2 |
3 | const BORDER_RADIUS = 14
4 | const BACKGROUND_COLOR = 'white'
5 |
6 | export default StyleSheet.create({
7 | contentContainer: {
8 | justifyContent: 'flex-end'
9 | },
10 | datepickerContainer: {
11 | backgroundColor: BACKGROUND_COLOR,
12 | borderRadius: BORDER_RADIUS
13 | },
14 | titleContainer: {
15 | borderBottomColor: 'rgba(0, 0, 0, 0.1)',
16 | borderBottomWidth: 1,
17 | padding: 12,
18 | backgroundColor: 'transparent'
19 | },
20 | title: {
21 | textAlign: 'center',
22 | color: 'grey',
23 | fontSize: 20
24 | },
25 | confirmButton: {
26 | borderTopColor: 'rgba(0, 0, 0, 0.1)',
27 | borderTopWidth: 1,
28 | padding: 10,
29 | backgroundColor: 'transparent'
30 | },
31 | confirmText: {
32 | textAlign: 'center',
33 | color: '#2E93FC',
34 | fontSize: 24,
35 | fontWeight: '500',
36 | backgroundColor: 'transparent'
37 | },
38 | cancelButton: {
39 | marginTop: 20,
40 | backgroundColor: BACKGROUND_COLOR,
41 | padding: 10,
42 | borderRadius: BORDER_RADIUS
43 | },
44 | cancelText: {
45 | textAlign: 'center',
46 | color: '#2E93FC',
47 | fontSize: 24,
48 | fontWeight: '700',
49 | backgroundColor: 'transparent'
50 | }
51 | })
52 |
--------------------------------------------------------------------------------
/src/CustomModal/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-return-assign */
2 | import React, { Component, PropTypes } from 'react'
3 | import { Modal } from 'react-native'
4 | import { View } from 'react-native-animatable'
5 |
6 | import styles from './index.style.js'
7 |
8 | export default class CustomModal extends Component {
9 | static propTypes = {
10 | visible: PropTypes.bool,
11 | children: PropTypes.node,
12 | contentContainerStyle: PropTypes.any
13 | }
14 |
15 | static defaultProps = {
16 | contentContainerStyle: {},
17 | visible: false
18 | }
19 |
20 | state = {
21 | visible: false
22 | }
23 |
24 | componentWillReceiveProps (nextProps) {
25 | if (!this.state.visible && nextProps.visible) {
26 | this.setState({ visible: true })
27 | }
28 | }
29 |
30 | componentDidUpdate (prevProps, prevState) {
31 | // On modal open request slide the view up and fade in the backdrop
32 | if (this.state.visible && !prevState.visible) {
33 | this._open()
34 | // On modal close request slide the view down and fade out the backdrop
35 | } else if (!this.props.visible && prevProps.visible) {
36 | this._close()
37 | }
38 | }
39 |
40 | _open = () => {
41 | this.backdropRef.transitionTo({ opacity: 0.70 })
42 | this.contentRef.slideInUp(300)
43 | }
44 |
45 | _close = () => {
46 | this.backdropRef.transitionTo({ opacity: 0 })
47 | this.contentRef.slideOutDown(300)
48 | .then(() => this.setState({ visible: false }))
49 | }
50 |
51 | render () {
52 | const { children, contentContainerStyle, ...otherProps } = this.props
53 | const { visible } = this.state
54 | return (
55 |
61 | this.backdropRef = ref} style={styles.backdrop} />
62 | this.contentRef = ref} style={[styles.contentContainer, contentContainerStyle]}>
63 | {children}
64 |
65 |
66 | )
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/CustomDatePickerAndroid/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import { DatePickerAndroid, TimePickerAndroid } from 'react-native'
3 | import moment from 'moment'
4 |
5 | export default class CustomDatePickerAndroid extends Component {
6 | static propTypes = {
7 | date: PropTypes.instanceOf(Date),
8 | mode: PropTypes.oneOf(['date', 'time']),
9 | onCancel: PropTypes.func.isRequired,
10 | onConfirm: PropTypes.func.isRequired,
11 | visible: PropTypes.bool
12 | }
13 |
14 | static defaultProps = {
15 | date: new Date(),
16 | mode: 'date',
17 | visible: false
18 | }
19 |
20 | componentDidUpdate = (prevProps) => {
21 | if (!prevProps.visible && this.props.visible) {
22 | if (this.props.mode === 'date') {
23 | this._showDatePickerAndroid()
24 | } else {
25 | this._showTimePickerAndroid()
26 | }
27 | }
28 | }
29 |
30 | _showDatePickerAndroid = async () => {
31 | try {
32 | const { action, year, month, day } = await DatePickerAndroid.open({
33 | date: this.props.date
34 | })
35 | if (action !== DatePickerAndroid.dismissedAction) {
36 | const date = moment({ year, month, day }).toDate()
37 | this.props.onConfirm(date)
38 | } else {
39 | this.props.onCancel()
40 | }
41 | } catch ({ code, message }) {
42 | console.warn('Cannot open date picker', message)
43 | }
44 | }
45 |
46 | _showTimePickerAndroid = async () => {
47 | try {
48 | const { action, hour, minute } = await TimePickerAndroid.open({
49 | hour: moment(this.props.date).hour(),
50 | minute: moment(this.props.date).minute(),
51 | is24Hour: true
52 | })
53 | if (action !== TimePickerAndroid.dismissedAction) {
54 | const date = moment({ hour, minute }).toDate()
55 | this.props.onConfirm(date)
56 | } else {
57 | this.props.onCancel()
58 | }
59 | } catch ({ code, message }) {
60 | console.warn('Cannot open time picker', message)
61 | }
62 | }
63 |
64 | render () {
65 | return null
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/CustomDatePickerIOS/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import { DatePickerIOS, Text, TouchableOpacity, View } from 'react-native'
3 | import CustomModal from '../CustomModal'
4 |
5 | import styles from './index.style'
6 |
7 | export default class CustomDatePickerIOS extends Component {
8 | static propTypes = {
9 | cancelTextIOS: PropTypes.string,
10 | confirmTextIOS: PropTypes.string,
11 | date: PropTypes.instanceOf(Date),
12 | mode: PropTypes.oneOf(['date', 'time']),
13 | onConfirm: PropTypes.func.isRequired,
14 | onCancel: PropTypes.func.isRequired,
15 | titleIOS: PropTypes.string,
16 | visible: PropTypes.bool
17 | }
18 |
19 | static defaultProps = {
20 | cancelTextIOS: 'Cancel',
21 | confirmTextIOS: 'Confirm',
22 | date: new Date(),
23 | mode: 'date',
24 | titleIOS: 'Pick a date',
25 | visible: false
26 | }
27 |
28 | state = {
29 | date: this.props.date
30 | }
31 |
32 | _handleConfirm = () => this.props.onConfirm(this.state.date)
33 |
34 | _handleDateChange = (date) => this.setState({ date })
35 |
36 | render () {
37 | const { onCancel, visible, mode, titleIOS, confirmTextIOS, cancelTextIOS, date, ...otherProps } = this.props
38 | return (
39 |
40 |
41 |
42 | {titleIOS}
43 |
44 |
50 |
51 | {confirmTextIOS}
52 |
53 |
54 |
55 | {cancelTextIOS}
56 |
57 |
58 | )
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # react-native-modal-datetime-picker
3 | A react-native datetime-picker that works on both Android and iOS.
4 |
5 |
6 |
7 | ## Description
8 | This library exposes a cross-platform interface for showing the native date and time pickers inside a modal.
9 | You will have an unified user experience, you won't have to worry anymore about testing the device platform and you won't have to programmatically call the Android TimePicker/DatePicker APIs.
10 |
11 |
12 | ## Setup
13 | This library is available on npm, install it with: `npm install --save react-native-modal-datetime-picker`.
14 |
15 | ## GIFs!
16 |
17 |
18 |
19 | ## Usage
20 | ```javascript
21 | import React, { Component, PropTypes } from 'react'
22 | import { Text, TouchableOpacity, View } from 'react-native'
23 | import DateTimePicker from 'react-native-modal-datetime-picker'
24 |
25 | export default class DateTimePickerTester extends Component {
26 | state = {
27 | isDateTimePickerVisible: false
28 | }
29 |
30 | _showDateTimePicker = () => this.setState({ isDateTimePickerVisible: true })
31 |
32 | _hideDateTimePicker = () => this.setState({ isDateTimePickerVisible: false })
33 |
34 | _handleDatePicked = (date) => {
35 | console.log('A date has been picked: ', date)
36 | this._hideDateTimePicker()
37 | }
38 |
39 | render () {
40 | return (
41 |
42 |
43 | Show TimePicker
44 |
45 |
50 |
51 | )
52 | }
53 |
54 | }
55 | ```
56 |
57 |
58 | ## Available props
59 | | Name | Type| Default | Description |
60 | | --- | --- | --- | --- |
61 | | visible | bool | false | Show the datetime picker? |
62 | | onConfirm | func | **REQUIRED** | Funcion called on date picked. |
63 | | onCancel | func | **REQUIRED** | Funcion called on dismiss. |
64 | | mode | string | 'date' | Datepicker? 'date' Timepicker? 'time' |
65 | | date | obj | new Date() | Initial selected date/time |
66 | | titleIOS | string | 'Pick a date' | The title text on iOS |
67 | | confirmTextIOS | string | 'Confirm' | The text on the confirm button on iOS |
68 | | cancelTextIOS | string | 'Cancel' | The text on the cancel button on iOS |
69 |
70 | All the [DatePickerIOS props](https://facebook.github.io/react-native/docs/datepickerios.html) are also supported!
71 |
72 | ## Notes
73 | Just remember to always set `visible` props to `false` in `onConfirm` and `onCancel` (like in the example above).
74 | Pull request and suggestions are welcome!
75 |
--------------------------------------------------------------------------------