UNPKG

react-native-web-ui-components

Version:

[![Dependencies](https://img.shields.io/badge/dependencies-renovate-brightgreen.svg)](https://github.com/CareLuLu/react-native-web-ui-components/issues/12) [![Codacy Badge](https://img.shields.io/codacy/grade/c0ef990240a84ab7abee7af64602dd6d/master)](http

300 lines (268 loc) 8.03 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import React, { useState, useRef, useEffect } from 'react'; import PropTypes from 'prop-types'; import { Platform, StyleSheet, Alert, Image, Modal, TouchableWithoutFeedback } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; import noop from 'lodash/noop'; import moment from 'moment'; import { withTheme } from '../Theme'; import Link from '../Link'; import View from '../View'; import Row from '../Row'; import Column from '../Column'; import TextInput from '../TextInput'; import TouchableOpacity from '../TouchableOpacity'; const styles = StyleSheet.create({ defaults: { minWidth: 100, height: 40 }, fullWidth: { width: '100%' }, icon: { position: 'absolute', top: 12, right: 10, width: 16, height: 16 }, overlay: { flex: 1, justifyContent: 'flex-end', backgroundColor: 'rgba(100, 100, 100, 0.7)' }, datepicker: { backgroundColor: '#FFFFFF' }, cancelColumn: { paddingLeft: 15 }, confirmColumn: { paddingRight: 15, alignItems: 'flex-end' }, link: { fontSize: 16, paddingTop: 10 } }); const DATE_FORMAT = 'MM/DD/YYYY'; const FORMAT = ['YYYY-MM-DD[T]HH:mm:ssZ', 'YYYY-MM-DD[T]HH:mm:ss.SSSZ', 'MM/DD/YYYY', 'MM/D/YYYY', 'M/D/YYYY', 'M/DD/YYYY', 'MM/DD/YYYY h:mma', 'MM/D/YYYY h:mma', 'M/D/YYYY h:mma', 'M/DD/YYYY h:mma', 'h:mma']; const icon = { uri: 'https://divin2sy6ce0b.cloudfront.net/images/calendar-icon.png' }; const getFormat = (format, mode) => { let currentFormat = format; if (!currentFormat) { switch (mode) { case 'time': currentFormat = 'h:mma'; break; case 'datetime': currentFormat = 'MM/DD/YYYY h:mma'; break; default: currentFormat = 'MM/DD/YYYY'; } } return currentFormat; }; const parseDate = (value, format) => { if (!value) { return null; } if (typeof value === 'string') { return moment(value, FORMAT.concat([format])).toDate(); } return value; }; const toString = (date, format) => { if (!date) { return ''; } return moment(date).format(format); }; const isSame = (date1, date2, format) => { if (!date1 && !date2) { return true; } return moment(date1).format(format) === moment(date2).format(format); }; const useSetDate = ({ format, onDateChange, excludeDates }) => { const setDate = newValue => { if (!newValue) { return onDateChange(''); } const value = moment(newValue); if (excludeDates) { const date = value.format(DATE_FORMAT); const notValid = excludeDates.filter(d => moment(parseDate(d, format)).format(DATE_FORMAT) === date).length; if (notValid) { setTimeout(() => Alert.alert('This date cannot be selected. Please choose another one.'), 500); return onDateChange(''); } } return onDateChange(value.format(format)); }; return setDate; }; const Datepicker = ({ date: value, onDateChange, mode, is24Hour, format, minDate, maxDate, excludeDates, animationType, supportedOrientations, ...props }) => { const { auto } = props; const currentFormat = getFormat(format, mode); const parsedValue = parseDate(value, currentFormat); const valueProp = useRef(parsedValue); const date = useRef(parsedValue || new Date()); const modal = useRef(); const [step, setStep] = useState(null); const setDate = useSetDate({ onDateChange, excludeDates, format: currentFormat }); const cancel = () => setStep(null); const clear = () => { setStep(null); setDate(''); }; const done = () => { setStep(null); setDate(date.current); }; const onPress = () => { if (step) { done(); } else { setStep('date'); } }; const onChange = (event, selected) => { date.current = selected || null; if (Platform.OS === 'android') { if (mode === 'datetime' && step === 'date') { setStep('transition'); } else { done(); } } }; const onModalRef = ref => { modal.current = ref; }; const params = {}; if (minDate) { params.minimumDate = moment(minDate, FORMAT.concat([currentFormat])).toDate(); } if (maxDate) { params.maximumDate = moment(maxDate, FORMAT.concat([currentFormat])).toDate(); } useEffect(() => { if (!isSame(valueProp.current, parsedValue, currentFormat)) { date.current = parsedValue || new Date(); valueProp.current = parsedValue; setTimeout(() => setStep(null)); } else if (step === 'transition') { setTimeout(() => setStep('time')); } }); return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TouchableOpacity, { onPress: onPress, style: auto ? null : styles.fullWidth }, /*#__PURE__*/React.createElement(View, { style: auto ? null : styles.fullWidth }, /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { editable: false, value: toString(parsedValue, format), onPress: onPress, pointerEvents: "none" })), /*#__PURE__*/React.createElement(Image, { source: icon, style: styles.icon }))), Platform.OS !== 'android' ? /*#__PURE__*/React.createElement(Modal, { transparent: true, ref: onModalRef, supportedOrientations: supportedOrientations, visible: step !== null && step !== 'transition', onRequestClose: cancel, animationType: animationType }, /*#__PURE__*/React.createElement(TouchableWithoutFeedback, { onPress: cancel }, /*#__PURE__*/React.createElement(View, { style: styles.overlay }, /*#__PURE__*/React.createElement(Row, { style: styles.datepicker }, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Column, { xs: 6, style: styles.cancelColumn }, /*#__PURE__*/React.createElement(Link, { auto: true, onPress: clear, style: styles.link }, "Clear")), /*#__PURE__*/React.createElement(Column, { xs: 6, style: styles.confirmColumn }, /*#__PURE__*/React.createElement(Link, { auto: true, onPress: done, style: styles.link }, "Confirm"))), /*#__PURE__*/React.createElement(DateTimePicker, _extends({}, params, { style: styles.fullWidth, value: date.current, is24Hour: is24Hour, mode: mode !== 'datetime' || Platform.OS !== 'android' ? mode : step, onChange: onChange, neutralButtonLabel: "Clear" })))))) : null, Platform.OS === 'android' && step && step !== 'transition' ? /*#__PURE__*/React.createElement(DateTimePicker, _extends({}, params, { style: styles.fullWidth, value: date.current, is24Hour: is24Hour, mode: mode !== 'datetime' || Platform.OS !== 'android' ? mode : step, onChange: onChange })) : null); }; Datepicker.propTypes = { auto: PropTypes.bool, date: PropTypes.any, // eslint-disable-line onDateChange: PropTypes.func, mode: PropTypes.oneOf(['date', 'datetime', 'time']), is24Hour: PropTypes.bool, format: PropTypes.string, minDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), maxDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), excludeDates: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)])), animationType: PropTypes.string, supportedOrientations: PropTypes.arrayOf(PropTypes.string) }; Datepicker.defaultProps = { auto: false, date: undefined, onDateChange: noop, mode: 'date', is24Hour: false, format: null, minDate: null, maxDate: null, excludeDates: [], animationType: 'slide', supportedOrientations: ['portrait', 'landscape'] }; export default withTheme('Datepicker')(Datepicker);