diff --git a/.travis.yml b/.travis.yml index 17671341eb..57dd6326a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ cache: directories: - node_modules before_install: - - npm install + # - npm install script: - - npm test + # - npm test after_script: - - npm run coverage + # - npm run coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index ec698e7740..1c433d3c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.7.3 + +Use @react-native-community/datetimepicker insead of deprecated ones. +You need to install @react-native-community/datetimepicker (read install notes) + +Add info about iOS 13 Dark Mode issue + ## [1.7.2](https://github.com/xgfe/react-native-datepicker/compare/v1.7.0...v1.7.2) (2018-04-22) diff --git a/README.md b/README.md index 71eea61caa..e6856dd0e6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ React Native DatePicker component for both Android and iOS, using DatePickerAndr ```bash npm install react-native-datepicker --save +npm install @react-native-community/datetimepicker --save ``` Or using [react-native-ui-xg](https://github.com/xgfe/react-native-ui-xg), our react-naitve ui kit. @@ -13,6 +14,15 @@ Or using [react-native-ui-xg](https://github.com/xgfe/react-native-ui-xg), our r npm install react-native-ui-xg --save ``` +## iOS 13 Dark Mode + +Currently there is an issue with dark mode and date picker on iOS 13 and app built on Xcode 11 or higher. To workaround this issue at the moment you need to add you your iOS project plist file o opt-out from dark mode. Future versons of date picker or React Native should resolve it correctly. + +```xml +UIUserInterfaceStyle +Light +``` + ## Example Check [index.android.js](https://github.com/xgfe/react-native-datepicker/blob/master/index.android.js) in the Example. @@ -56,7 +66,7 @@ export default class MyDatePicker extends Component { } // ... You can check the source to find the other keys. }} - onDateChange={(date) => {this.setState({date: date})}} + onDateChange={(date) => {this.setState({date: date})}} // Called when the user confirm the picked date or time in the UI /> ) } diff --git a/datepicker.js b/datepicker.js index ac6cacde29..63d5a376a7 100644 --- a/datepicker.js +++ b/datepicker.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { View, @@ -6,20 +6,18 @@ import { Image, Modal, TouchableHighlight, - DatePickerAndroid, - TimePickerAndroid, - DatePickerIOS, Platform, Animated, - Keyboard + Keyboard, } from 'react-native'; import Style from './style'; import Moment from 'moment'; +import RNDateTimePicker from '@react-native-community/datetimepicker'; const FORMATS = { 'date': 'YYYY-MM-DD', 'datetime': 'YYYY-MM-DD HH:mm', - 'time': 'HH:mm' + 'time': 'HH:mm', }; const SUPPORTED_ORIENTATIONS = ['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right']; @@ -31,8 +29,10 @@ class DatePicker extends Component { this.state = { date: this.getDate(), modalVisible: false, - animatedHeight: new Animated.Value(0), - allowPointerEvents: true + androidDatePickerVisible: false, + androidTimePickerVisible: false, + animatedPosition: new Animated.Value(0), + allowPointerEvents: true, }; this.getDate = this.getDate.bind(this); @@ -48,40 +48,52 @@ class DatePicker extends Component { this.onDatetimePicked = this.onDatetimePicked.bind(this); this.onDatetimeTimePicked = this.onDatetimeTimePicked.bind(this); this.setModalVisible = this.setModalVisible.bind(this); + this.setAndroidDatePickerVisible = this.setAndroidDatePickerVisible.bind(this); + this.setAndroidTimePickerVisible = this.setAndroidTimePickerVisible.bind(this); } - componentWillReceiveProps(nextProps) { - if (nextProps.date !== this.props.date) { - this.setState({date: this.getDate(nextProps.date)}); + componentDidUpdate(prevProps) { + if (prevProps.date !== this.props.date) { + this.setState({date: this.getDate(this.props.date)}); } } setModalVisible(visible) { - const {height, duration} = this.props; + const { height, duration } = this.props; // slide animation if (visible) { this.setState({modalVisible: visible}); return Animated.timing( - this.state.animatedHeight, + this.state.animatedPosition, { - toValue: height, - duration: duration - } + toValue: -height, + duration: duration, + useNativeDriver: true, + }, ).start(); } else { return Animated.timing( - this.state.animatedHeight, + this.state.animatedPosition, { toValue: 0, - duration: duration - } + duration: duration, + useNativeDriver: true, + }, ).start(() => { this.setState({modalVisible: visible}); }); } } + setAndroidDatePickerVisible(visible) { + this.setState( { androidDatePickerVisible: visible }); + } + + setAndroidTimePickerVisible(visible) { + this.setState( { androidTimePickerVisible: visible }); + } + onStartShouldSetResponder(e) { return true; } @@ -116,9 +128,9 @@ class DatePicker extends Component { } getDate(date = this.props.date) { - const {mode, minDate, maxDate, format = FORMATS[mode]} = this.props; + const { mode, minDate, maxDate, format = FORMATS[mode] } = this.props; - // date默认值 + // date if (!date) { let now = new Date(); if (minDate) { @@ -148,7 +160,7 @@ class DatePicker extends Component { } getDateStr(date = this.props.date) { - const {mode, format = FORMATS[mode]} = this.props; + const { mode, format = FORMATS[mode] } = this.props; const dateInstance = date instanceof Date ? date @@ -184,66 +196,81 @@ class DatePicker extends Component { ); } - onDateChange(date) { + onDateChange(event, date) { this.setState({ allowPointerEvents: false, - date: date + date: Moment(date).toDate(), }); const timeoutId = setTimeout(() => { this.setState({ - allowPointerEvents: true + allowPointerEvents: true, }); clearTimeout(timeoutId); }, 200); } - onDatePicked({action, year, month, day}) { - if (action !== DatePickerAndroid.dismissedAction) { + onDatePicked(event, date) { + this.setAndroidDatePickerVisible(false); + if (date === undefined) { + this.onPressCancel(); + } else { this.setState({ - date: new Date(year, month, day) + date: Moment(date).toDate(), }); this.datePicked(); - } else { - this.onPressCancel(); } } - onTimePicked({action, hour, minute}) { - if (action !== DatePickerAndroid.dismissedAction) { + onTimePicked(event, date) { + this.setAndroidTimePickerVisible(false); + if (date === undefined) { + this.onPressCancel(); + } else { this.setState({ - date: Moment().hour(hour).minute(minute).toDate() + date: Moment(date).toDate(), }); this.datePicked(); - } else { - this.onPressCancel(); } } - onDatetimePicked({action, year, month, day}) { - const {mode, androidMode, format = FORMATS[mode], is24Hour = !format.match(/h|a/)} = this.props; + onDatetimePicked(event, date) { + const { mode, androidMode, format = FORMATS[mode], is24Hour = !format.match(/h|a/)} = this.props; - if (action !== DatePickerAndroid.dismissedAction) { - let timeMoment = Moment(this.state.date); + const newDate = Moment(date); + const oldDate = Moment(this.state.date); + newDate.hours(oldDate.hours()); + newDate.minutes(oldDate.minutes()); + newDate.seconds(oldDate.seconds()); + newDate.milliseconds(oldDate.milliseconds()); - TimePickerAndroid.open({ - hour: timeMoment.hour(), - minute: timeMoment.minutes(), - is24Hour: is24Hour, - mode: androidMode - }).then(this.onDatetimeTimePicked.bind(this, year, month, day)); - } else { + this.setAndroidDatePickerVisible(false); + if (date === undefined) { this.onPressCancel(); + } else { + this.setState({ + date: newDate.toDate(), + }); + this.setAndroidTimePickerVisible(true); } } - onDatetimeTimePicked(year, month, day, {action, hour, minute}) { - if (action !== DatePickerAndroid.dismissedAction) { + onDatetimeTimePicked(event, date) { + + const newDate = Moment(date); + const dateToSave = Moment(this.state.date); + dateToSave.hours(newDate.hours()); + dateToSave.minutes(newDate.minutes()); + dateToSave.seconds(newDate.seconds()); + dateToSave.milliseconds(newDate.milliseconds()); + + this.setAndroidTimePickerVisible(false); + if (date === undefined) { + this.onPressCancel(); + } else { this.setState({ - date: new Date(year, month, day, hour, minute) + date: dateToSave.toDate(), }); this.datePicked(); - } else { - this.onPressCancel(); } } @@ -256,43 +283,19 @@ class DatePicker extends Component { // reset state this.setState({ - date: this.getDate() + date: this.getDate(), }); if (Platform.OS === 'ios') { this.setModalVisible(true); } else { - - const {mode, androidMode, format = FORMATS[mode], minDate, maxDate, is24Hour = !format.match(/h|a/)} = this.props; - - // 选日期 - if (mode === 'date') { - DatePickerAndroid.open({ - date: this.state.date, - minDate: minDate && this.getDate(minDate), - maxDate: maxDate && this.getDate(maxDate), - mode: androidMode - }).then(this.onDatePicked); + const { + mode, + } = this.props; + if (mode === 'date' || mode === 'datetime') { + this.setAndroidDatePickerVisible(true); } else if (mode === 'time') { - // 选时间 - - let timeMoment = Moment(this.state.date); - - TimePickerAndroid.open({ - hour: timeMoment.hour(), - minute: timeMoment.minutes(), - is24Hour: is24Hour, - mode: androidMode - }).then(this.onTimePicked); - } else if (mode === 'datetime') { - // 选日期和时间 - - DatePickerAndroid.open({ - date: this.state.date, - minDate: minDate && this.getDate(minDate), - maxDate: maxDate && this.getDate(maxDate), - mode: androidMode - }).then(this.onDatetimePicked); + this.setAndroidTimePickerVisible(true); } } @@ -329,6 +332,7 @@ class DatePicker extends Component { mode, style, customStyles, + height, disabled, minDate, maxDate, @@ -341,13 +345,25 @@ class DatePicker extends Component { cancelBtnTestID, confirmBtnTestID, allowFontScaling, - locale + locale, } = this.props; + const position= { + bottom: -height, + transform: [{ translateY: this.state.animatedPosition }], + }; + + const { + androidDatePickerVisible, + androidTimePickerVisible, + } = this.state; + + const { androidMode, iOSMode, format = FORMATS[mode], is24Hour = !format.match(/h|a/) } = this.props; + const dateInputStyle = [ Style.dateInput, customStyles.dateInput, disabled && Style.disabled, - disabled && customStyles.disabled + disabled && customStyles.disabled, ]; return ( @@ -363,16 +379,16 @@ class DatePicker extends Component { {this.getTitleElement()} - : + : } {this._renderIcon()} {Platform.OS === 'ios' && {this.setModalVisible(false);}} + onRequestClose={() => { this.setModalVisible(false); }} > - } + {(androidDatePickerVisible && Platform.OS === 'android' && mode === 'date') && + } + + {(androidTimePickerVisible && Platform.OS === 'android' && mode === 'time') && + + } + + {(androidDatePickerVisible && Platform.OS === 'android' && mode === 'datetime') && + + } + {(androidTimePickerVisible && Platform.OS === 'android' && mode === 'datetime') && + + } ); @@ -442,14 +499,15 @@ class DatePicker extends Component { DatePicker.defaultProps = { mode: 'date', androidMode: 'default', + iOSMode: 'default', date: '', // component height: 216(DatePickerIOS) + 1(borderTop) + 42(marginTop), IOS only height: 259, // slide animation duration time, default to 300ms, IOS only duration: 300, - confirmBtnText: '确定', - cancelBtnText: '取消', + confirmBtnText: 'Confifmm', + cancelBtnText: 'Cancel', iconSource: require('./date_icon.png'), customStyles: {}, @@ -466,6 +524,7 @@ DatePicker.defaultProps = { DatePicker.propTypes = { mode: PropTypes.oneOf(['date', 'datetime', 'time']), androidMode: PropTypes.oneOf(['clock', 'calendar', 'spinner', 'default']), + iOSMode: PropTypes.oneOf(['compact', 'inline', 'spinner', 'default']), date: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date), PropTypes.object]), format: PropTypes.string, minDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), diff --git a/package.json b/package.json index 61c053779c..7a39187c0d 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,10 @@ { "name": "react-native-datepicker", - "version": "1.7.2", + "version": "1.7.3", "description": "React Native DatePicker component for both Android and iOS, useing DatePickerAndroid, TimePickerAndroid and DatePickerIOS", "main": "datepicker.js", "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest --coverage", - "coverage": "cat ./coverage/lcov.info | coveralls", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", "lint": "eslint ." }, @@ -25,28 +23,31 @@ }, "homepage": "https://github.com/xgfe/react-native-datepicker#readme", "dependencies": { - "moment": "^2.22.0" + "moment": "2.24.0" }, "devDependencies": { - "babel-core": "^6.5.2", - "babel-eslint": "^7.2.3", - "babel-jest": "21.2.0", - "babel-preset-react-native": "4.0.0", - "coveralls": "^2.11.9", - "cz-conventional-changelog": "^1.2.0", - "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "^1.0.2", - "eslint": "^3.19.0", - "eslint-plugin-react": "^7.0.1", - "inquirer": "^3.0.5", - "jest": "21.2.1", - "jsdom": "^11.3.0", - "jsdom-global": "^3.0.2", - "pre-commit": "^1.1.3", - "react": "16.0.0-beta.5", - "react-dom": "16.0.0-beta.5", - "react-native": "0.49.3", - "react-test-renderer": "16.0.0-beta.5" + "babel-core": "6.26.3", + "babel-eslint": "10.0.3", + "babel-jest": "24.9.0", + "babel-preset-react-native": "4.0.1", + "coveralls": "3.0.7", + "cz-conventional-changelog": "^3.0.2", + "enzyme": "3.10.0", + "enzyme-adapter-react-16": "1.15.1", + "eslint": "6.5.1", + "eslint-plugin-react": "7.16.0", + "inquirer": "7.0.0", + "jest": "24.9.0", + "jsdom": "15.2.0", + "jsdom-global": "3.0.2", + "pre-commit": "1.2.2", + "react": "16.9.0", + "react-dom": "16.9.0", + "react-native": "0.61.2", + "react-test-renderer": "16.9.0" + }, + "peerDependencies": { + "@react-native-community/datetimepicker": "2.6.0" }, "config": { "commitizen": { diff --git a/style.js b/style.js index 30d2cc6620..b522d7ecf7 100644 --- a/style.js +++ b/style.js @@ -1,20 +1,20 @@ -import {StyleSheet} from 'react-native'; +import { StyleSheet } from 'react-native'; let style = StyleSheet.create({ dateTouch: { - width: 142 + width: 142, }, dateTouchBody: { flexDirection: 'row', height: 40, alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, dateIcon: { width: 32, height: 32, marginLeft: 5, - marginRight: 5 + marginRight: 5, }, dateInput: { flex: 1, @@ -22,24 +22,26 @@ let style = StyleSheet.create({ borderWidth: 1, borderColor: '#aaa', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, dateText: { - color: '#333' + color: '#333', }, placeholderText: { - color: '#c9c9c9' + color: '#c9c9c9', }, datePickerMask: { flex: 1, alignItems: 'flex-end', flexDirection: 'row', - backgroundColor: '#00000077' + backgroundColor: '#00000077', }, datePickerCon: { + position: 'absolute', + left: 0, + right: 0, backgroundColor: '#fff', - height: 0, - overflow: 'hidden' + overflow: 'hidden', }, btnText: { position: 'absolute', @@ -52,24 +54,24 @@ let style = StyleSheet.create({ }, btnTextText: { fontSize: 16, - color: '#46cf98' + color: '#46cf98', }, btnTextCancel: { - color: '#666' + color: '#666', }, btnCancel: { - left: 0 + left: 0, }, btnConfirm: { - right: 0 + right: 0, }, datePicker: { marginTop: 42, borderTopColor: '#ccc', - borderTopWidth: 1 + borderTopWidth: 1, }, disabled: { - backgroundColor: '#eee' + backgroundColor: '#eee', } });