react-native-ui-lib
Version:
<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a
296 lines (256 loc) • 8.44 kB
JavaScript
import _pt from "prop-types";
import _ from 'lodash';
import moment from 'moment';
import React, { Component } from 'react';
import { StyleSheet } from 'react-native';
import { DateTimePickerPackage as RNDateTimePicker } from "../../optionalDependencies";
import { Colors } from "../../style";
import Assets from "../../assets";
import { Constants, asBaseComponent } from "../../commons/new";
import TextField from "../textField";
import Dialog from "../dialog";
import View from "../view";
import Button from "../button";
const MODES = {
DATE: 'date',
TIME: 'time'
};
/*eslint-disable*/
/**
* @description: Date and Time Picker Component that wraps RNDateTimePicker for date and time modes.
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/DateTimePickerScreen.js
* @important: DateTimePicker uses a native library. You MUST add and link the native library to both iOS and Android projects.
* @extends: TextField, react-native-community/datetimepicker
* @extendsLink: https://github.com/react-native-community/react-native-datetimepicker#react-native-datetimepicker
* @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/DateTimePicker/DateTimePicker_iOS.gif?raw=true, https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/DateTimePicker/DateTimePicker_Android.gif?raw=true
*/
/*eslint-enable*/
class DateTimePicker extends Component {
static propTypes = {
/**
* The type of picker to display ('date' or 'time')
*/
/* ...TextField.propTypes,*/
/* TODO: extend TextField props*/
mode: _pt.oneOf(['date', 'time']),
/**
* The initial value to set the picker to. Defaults to device's date / time
*/
value: _pt.instanceOf(Date),
/**
* The onChange callback
*/
onChange: _pt.func,
/**
* The minimum date or time value to use
*/
minimumDate: _pt.instanceOf(Date),
/**
* The maximum date or time value to use
*/
maximumDate: _pt.instanceOf(Date),
/**
* The date format for the text display
*/
dateFormat: _pt.string,
/**
* A callback function to format date
*/
dateFormatter: _pt.func,
/**
* The time format for the text display
*/
timeFormat: _pt.string,
/**
* A callback function to format time
*/
timeFormatter: _pt.func,
/**
* Allows changing of the locale of the component (iOS only)
*/
locale: _pt.string,
/**
* Allows changing of the time picker to a 24 hour format (Android only)
*/
is24Hour: _pt.bool,
/**
* The interval at which minutes can be selected. Possible values are: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 (iOS only)
*/
minuteInterval: _pt.number,
/**
* Allows changing of the timeZone of the date picker. By default it uses the device's time zone (iOS only)
*/
timeZoneOffsetInMinutes: _pt.number,
/**
* Render custom input
*/
renderInput: _pt.func,
/**
* Override system theme variant (dark or light mode) used by the date picker.
*/
themeVariant: _pt.oneOf(['light', 'dark']),
/**
* The component testID
*/
testID: _pt.string
};
static displayName = 'DateTimePicker';
static defaultProps = { ...TextField.defaultProps,
mode: MODES.DATE
};
constructor(props) {
super(props);
this.chosenDate = props.value;
this.state = {
showExpandableOverlay: false,
prevValue: props.value,
value: props.value
};
if (!RNDateTimePicker) {
console.error(`RNUILib DateTimePicker component requires installing "@react-native-community/datetimepicker" dependency`);
}
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.value !== prevState.prevValue) {
return {
prevValue: prevState.value,
value: nextProps.value
};
}
return null;
}
handleChange = (event = {}, date) => {
// NOTE: will be called on Android even when there was no actual change
if (event.type !== 'dismissed' && date !== undefined) {
this.chosenDate = date;
if (Constants.isAndroid) {
this.onDonePressed();
}
} else if (event.type === 'dismissed' && Constants.isAndroid) {
this.toggleExpandableOverlay();
}
};
toggleExpandableOverlay = callback => {
this.setState({
showExpandableOverlay: !this.state.showExpandableOverlay
}, () => {
if (_.isFunction(callback)) {
callback();
}
});
};
onToggleExpandableModal = value => {
this.toggleExpandableOverlay();
_.invoke(this.props, 'onToggleExpandableModal', value);
};
onDonePressed = () => this.toggleExpandableOverlay(() => {
if (Constants.isIOS && !this.chosenDate) {
// since handleChange() is not called on iOS when there is no actual change
this.chosenDate = new Date();
}
_.invoke(this.props, 'onChange', this.chosenDate);
this.setState({
value: this.chosenDate
});
});
getStringValue = () => {
const {
value
} = this.state;
const {
mode,
dateFormat,
timeFormat,
dateFormatter,
timeFormatter
} = this.props;
if (value) {
switch (mode) {
case MODES.DATE:
return dateFormatter ? dateFormatter(value) : dateFormat ? moment(value).format(dateFormat) : value.toLocaleDateString();
case MODES.TIME:
return timeFormatter ? timeFormatter(value) : timeFormat ? moment(value).format(timeFormat) : value.toLocaleTimeString();
}
}
};
renderExpandableOverlay = () => {
const {
testID,
dialogProps
} = this.props;
const {
showExpandableOverlay
} = this.state;
return <Dialog visible={showExpandableOverlay} width="100%" height={null} bottom centerH onDismiss={this.toggleExpandableOverlay} containerStyle={styles.dialog} testID={`${testID}.dialog`} supportedOrientations={['portrait', 'landscape', 'landscape-left', 'landscape-right']} // iOS only
{...dialogProps}>
<View
/* useSafeArea */
>
{this.renderHeader()}
{this.renderDateTimePicker()}
</View>
</Dialog>;
};
renderHeader() {
// @ts-expect-error
const {
headerStyle,
useCustomTheme
} = this.props;
return <View row spread bg-white paddingH-20 style={[styles.header, headerStyle]}>
<Button link iconSource={Assets.icons.x} iconStyle={{
tintColor: Colors.grey10
}} onPress={this.toggleExpandableOverlay} />
<Button link iconSource={Assets.icons.check} useCustomTheme={useCustomTheme} onPress={this.onDonePressed} />
</View>;
}
renderDateTimePicker() {
if (!RNDateTimePicker) {
return null;
}
const {
value,
showExpandableOverlay
} = this.state;
const {
mode,
minimumDate,
maximumDate,
locale,
is24Hour,
minuteInterval,
timeZoneOffsetInMinutes,
themeVariant
} = this.props;
if (showExpandableOverlay) {
return <RNDateTimePicker mode={mode} value={value || new Date()} onChange={this.handleChange} minimumDate={minimumDate} maximumDate={maximumDate} locale={locale} is24Hour={is24Hour} minuteInterval={minuteInterval} timeZoneOffsetInMinutes={timeZoneOffsetInMinutes} display={Constants.isIOS ? 'spinner' : undefined} themeVariant={themeVariant} />;
}
}
renderExpandable = () => {
return Constants.isAndroid ? this.renderDateTimePicker() : this.renderExpandableOverlay();
};
render() {
// @ts-expect-error
const textInputProps = TextField.extractOwnProps(this.props);
const {
renderInput
} = this.props;
return (// @ts-expect-error
<TextField renderExpandableInput={renderInput} {...textInputProps} value={this.getStringValue()} expandable renderExpandable={this.renderExpandable} onToggleExpandableModal={this.onToggleExpandableModal} />
);
}
}
export { DateTimePicker }; // For tests
export default asBaseComponent(DateTimePicker);
const styles = StyleSheet.create({
header: {
height: 56,
borderBottomWidth: 1,
borderBottomColor: Colors.grey80
},
dialog: {
backgroundColor: Colors.white,
borderTopLeftRadius: 12,
borderTopRightRadius: 12
}
});