Created
May 7, 2017 23:59
-
-
Save alien3d/ccea232fbaa4cd0697909d7c18797474 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { PropTypes, Component } from 'react'; | |
import { | |
ScrollView, | |
View, | |
StyleSheet, | |
TouchableHighlight, | |
Image, | |
Platform, | |
Switch, | |
TextInput, | |
DatePickerAndroid, | |
DatePickerIOS, | |
TimePickerAndroid, | |
TouchableWithoutFeedback, | |
TouchableOpacity, | |
Animated, | |
Modal | |
} from 'react-native'; | |
import Badge from '../badge/badge'; | |
import Icon from '../icons/Icon'; | |
import Text from '../text/Text'; | |
import colors from '../config/colors'; | |
import fonts from '../config/fonts'; | |
import normalize from '../helpers/normalizeText'; | |
import Moment from 'moment'; | |
let styles; | |
const FORMATS = { | |
'date': 'YYYY-MM-DD', | |
'datetime': 'YYYY-MM-DD HH:mm', | |
'time': 'HH:mm' | |
}; | |
const SUPPORTED_ORIENTATIONS = ['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right']; | |
class ListItem extends Component { | |
constructor(props) { | |
super(props); | |
var j = new Date(); | |
this.state = { | |
currentDateTime: j.getFullYear() + (j.getMonth() + 1) + j.getDay() + " " + j.getHours() + ":" + j.getMinutes(), | |
currentTime: this._formatTime(j.getHours(), + j.getMinutes()), | |
currentHour: j.getHours(), | |
currentMinute: j.getMinutes(), | |
is24Hour: false, | |
date: j, | |
timeZoneOffsetInHours: (-1) * (new Date()).getTimezoneOffset() / 60, | |
modalVisible: false, | |
animatedHeight: new Animated.Value(0) | |
} | |
} | |
showPickerAndroid = async (options) => { | |
try { | |
var newState = {}; | |
const { action, year, month, day } = await DatePickerAndroid.open(options); | |
if (action === DatePickerAndroid.dismissedAction) { | |
newState['currentText'] = 'dismissed'; | |
} else { | |
var date = new Date(year, month, day); | |
newState['currentText'] = date.toLocaleDateString(); | |
newState['currentDate'] = date; | |
this | |
.props | |
.textInputDateOnChange(date); | |
} | |
this.setState(newState); | |
} catch ({ code, message }) { | |
console.warn(`Error `, message); | |
} | |
}; | |
showTimePickerAndroid = async (options) => { | |
try { | |
const { action, minute, hour } = await TimePickerAndroid.open(options); | |
var newState = {}; | |
if (action === TimePickerAndroid.timeSetAction) { | |
newState['currentTime'] = this._formatTime(hour, minute); | |
newState['currentHour'] = hour; | |
newState['currentMinute'] = minute; | |
this | |
.props | |
.textInputTimeOnChange(this._formatTime(hour, minute)); | |
} else if (action === TimePickerAndroid.dismissedAction) { | |
newState['currentText'] = 'dismissed'; | |
} | |
this.setState(newState); | |
} catch ({ code, message }) { | |
console.warn(`Error `, message); | |
} | |
}; | |
/** | |
* no prefer this style method | |
onDateChange = (date) => { | |
this.setState({ date: date }); | |
}; | |
**/ | |
onDateChange(date) { | |
this.setState({ date: date }); | |
return date; | |
} | |
onTimezoneChange = (event) => { | |
var offset = parseInt(event.nativeEvent.text, 10); | |
if (isNaN(offset)) { | |
return; | |
} | |
this.setState({ timeZoneOffsetInHours: offset }); | |
}; | |
_formatTime(hour, minute) { | |
return hour + ':' + (minute < 10 | |
? '0' + minute | |
: minute); | |
} | |
/** | |
* Format Date. Either using default or pre-defined | |
* @param {object} date | |
* @param {type} dateType | |
* @param {object} format | |
* @returns {string} date | |
*/ | |
_formatDate(date, type, format) { | |
dateParse = null; | |
// if js can do like this.. could be shorter FORMATS.{type} ? | |
var dateParse = null; | |
// use pre defined format | |
if (typeof format !== 'object') { | |
switch (type) { | |
case "date": | |
dateParse = Moment(date, FORMATS.date).format(FORMATS.date); | |
break; | |
case "datetime": | |
dateParse = Moment(date, FORMATS.datetime).format(FORMATS.datetime); | |
break; | |
case "time": | |
dateParse = Moment(date, FORMATS.time).format(FORMATS.time); | |
break; | |
case "hour": | |
if (date.length == 1) { | |
dateParse = "0".date; | |
} else { | |
dataParse = date; | |
} | |
break; | |
case "minute": | |
if (date.length == 1) { | |
dateParse = "0".date; | |
} else { | |
dataParse = date; | |
} | |
break; | |
default: | |
dateParse = "1.4 : unknown custom date"; | |
} | |
} else { | |
switch (type) { | |
case "date": | |
dateParse = Moment(date, format.date); | |
break; | |
case "datetime": | |
dateParse = Moment(date, format.datetime); | |
break; | |
case "time": | |
dateParse = Moment(date, format.time); | |
break; | |
case "hour": | |
if (date.length == 1) { | |
dateParse = "0".date; | |
} else { | |
dataParse = date; | |
} | |
break; | |
case "minute": | |
if (date.length == 1) { | |
dateParse = "0".date; | |
} else { | |
dataParse = date; | |
} | |
break; | |
default: | |
dateParse = "2.4 unknown custom date"; | |
} | |
} | |
return dateParse; | |
} | |
/** | |
* TextInput Focus | |
* @returns {void} | |
*/ | |
focus() { | |
const ref = this.props.textInputRef; | |
this | |
.refs[ref] | |
.focus(); | |
} | |
/** | |
* TextInput Blur | |
* @returns {void} | |
*/ | |
blur() { | |
const ref = this.props.textInputRef; | |
this | |
.refs[ref] | |
.blur(); | |
} | |
/** | |
* Switch Value | |
* @param {bool} value | |
*/ | |
_onSwitchChange(value) { | |
this | |
.props | |
.switchOnChange(value); | |
} | |
render() { | |
const { | |
onPress, | |
title, | |
leftIcon, | |
rightIcon, | |
leftIconContainerStyle, | |
avatarStyle, | |
underlayColor, | |
subtitle, | |
subtitleStyle, | |
containerStyle, | |
wrapperStyle, | |
titleStyle, | |
titleContainerStyle, | |
hideChevron, | |
chevronColor, | |
chevronOnPress, | |
roundAvatar, | |
component, | |
fontFamily, | |
rightTitle, | |
rightTitleContainerStyle, | |
rightTitleStyle, | |
subtitleContainerStyle, | |
badge, | |
badgeContainerStyle, | |
badgeTextStyle, | |
label, | |
onLongPress, | |
switchButton, | |
onSwitch, | |
switchDisabled, | |
switchOnTintColor, | |
switchThumbTintColor, | |
switchTintColor, | |
switched, | |
textInput, | |
textInputRef, | |
textInputSecureTextEntry, | |
textInputAutoCapitalize, | |
textInputAutoCorrect, | |
textInputFocus, | |
textInputAutoFocus, | |
textInputEditable, | |
textInputKeyboardType, | |
textInputMaxLength, | |
textInputMultiline, | |
textInputOnChangeText, | |
textInputOnFocus, | |
textInputOnBlur, | |
textInputOnSubmitEditing, | |
textInputSelectTextOnFocus, | |
textInputReturnKeyType, | |
textInputValue, | |
textInputStyle, | |
textInputContainerStyle, | |
textInputPlaceHolder, | |
textInputHour, | |
textInputHourFormat, | |
textInputMinute, | |
textInputMinuteFormat, | |
textInputTime, | |
textInputTimeFormat, | |
textInputTimeIs24Hour, | |
textInputDate, | |
textInputDateFormat, | |
textInputDateMode, | |
textInputDateMinDate, | |
textInputDateMaxDate, | |
textInputDateTime, | |
textInputDateTimeFormat, | |
...attributes | |
} = this.props; | |
let { avatar } = this.props; | |
if (typeof avatar === 'string') { | |
avatar = { | |
uri: avatar | |
} | |
} | |
if (typeof textInputDate === "string") { | |
// just double recheck from props.. | |
if (textInputDate.length > 0) { | |
// check valid date and is format | |
if (Moment(textInputDate, textInputDateFormat, true).isValid()) { | |
this.setState({ currentDate: textInputDate }); | |
} else { | |
console.log("Date give is not in right format. Date : " + textInputDate + " Format " + textInputDateFormat); | |
} | |
} | |
} | |
if (typeof textInputDateTime === "string") { | |
// just double recheck from props.. | |
if (textInputDateTime.length > 0) { | |
// check valid date and is format | |
if (Moment(textInputDateTime, textInputDateTimeFormat, true).isValid()) { | |
this.setState({ currentDateTime: textInputDateTime }); | |
} else { | |
console.log("Date give is not in right format. Date : " + textInputDateTime + " Format " + textInputDateTimeFormat); | |
} | |
} | |
} | |
if (typeof textInputHour === "string") { | |
if (textInputHour.length > 0) { | |
if (Moment(textInputHour, textInputHourFormat, true).isValid()) { | |
this.setState({ currentHour: textInputHour }); | |
} | |
} | |
} | |
// unknown yet output should be | |
if (typeof textInputMinute === "string") { | |
if (textInputMinute.length > 0) { | |
if (Moment(textInputHour, textInputTimeFormat, true).isValid()) { | |
this.setState({ currentMinute: textInputMinute }); | |
} | |
} | |
} | |
if (typeof textInputTimeIs24Hour === "string") { | |
// just double recheck from props.. | |
if (textInputTimeIs24Hour.length > 0) { | |
if (textInputTimeIs24Hour == "true" || textInputTimeIs24Hour == "false" || textInputTimeIs24Hour == true || textInputTimeIs24Hour == false) { | |
this.setState({ is24Hour: textInputTimeIs24Hour }); | |
} | |
} | |
} | |
return ( | |
<TouchableHighlight | |
onLongPress={onLongPress} | |
onPress={onPress} | |
underlayColor={underlayColor} | |
style={[ | |
styles.container, containerStyle && containerStyle | |
]} | |
{...attributes}> | |
<View | |
style={[ | |
styles.wrapper, wrapperStyle && wrapperStyle | |
]}> | |
{leftIcon && leftIcon.name && ( | |
<View | |
style={[ | |
styles.iconStyle, leftIconContainerStyle && leftIconContainerStyle | |
]}><Icon | |
type={leftIcon.type} | |
iconStyle={[ | |
styles.icon, leftIcon.style && leftIcon.style | |
]} | |
name={leftIcon.name} | |
color={leftIcon.color || colors.grey4} | |
size={leftIcon.size || 24} /> | |
</View> | |
) | |
} | |
{avatar && (<Image | |
style={[ | |
styles.avatar, roundAvatar && { | |
borderRadius: 17 | |
}, | |
avatarStyle && avatarStyle | |
]} | |
source={avatar} />) | |
} | |
<View style={styles.titleSubtitleContainer}> | |
<View style={titleContainerStyle}> | |
{(title && (typeof title === 'string' || typeof title === 'number')) | |
? ( | |
<Text | |
style={[ | |
styles.title, !leftIcon && { | |
marginLeft: 10 | |
}, | |
titleStyle && titleStyle, | |
fontFamily && { | |
fontFamily | |
} | |
]}>{title}</Text> | |
) | |
: ( | |
<View> | |
{title} | |
</View> | |
)} | |
</View> | |
<View style={subtitleContainerStyle}> | |
{(subtitle && (typeof subtitle === 'string' || typeof subtitle === 'number')) | |
? ( | |
<Text | |
style={[ | |
styles.subtitle, !leftIcon && { | |
marginLeft: 10 | |
}, | |
subtitleStyle && subtitleStyle, | |
fontFamily && { | |
fontFamily | |
} | |
]}>{subtitle}</Text> | |
) | |
: ( | |
<View> | |
{subtitle} | |
</View> | |
)} | |
</View> | |
</View> | |
{rightTitle && (rightTitle !== '') && !textInput && ( | |
<View style={[styles.rightTitleContainer, rightTitleContainerStyle]}> | |
<Text style={[styles.rightTitleStyle, rightTitleStyle]}>{rightTitle}</Text> | |
</View> | |
) | |
} | |
{textInput && ( | |
<View style={textInputContainerStyle}> | |
<TextInput | |
ref={textInputRef} | |
secureTextEntry={textInputSecureTextEntry} | |
underlineColorAndroid='transparent' | |
style={textInputStyle} | |
defaultValue={rightTitle} | |
value={textInputValue} | |
autoCapitalize={textInputAutoCapitalize} | |
autoCorrect={textInputAutoCorrect} | |
autoFocus={textInputAutoFocus} | |
focus={textInputFocus} | |
editable={textInputEditable} | |
keyboardType={textInputKeyboardType} | |
maxLength={textInputMaxLength} | |
multiline={textInputMultiline} | |
onChangeText={textInputOnChangeText} | |
onFocus={textInputOnFocus} | |
onBlur={textInputOnBlur} | |
onSubmitEditing={textInputOnSubmitEditing} | |
selectTextOnFocus={textInputSelectTextOnFocus} | |
returnKeyType={textInputReturnKeyType} | |
placeholder={textInputPlaceHolder} /> | |
</View> | |
) | |
} | |
{Platform.OS === 'android' && textInputDate && ( | |
<View style={textInputContainerStyle}> | |
<TouchableWithoutFeedback | |
onPress={this | |
.showPickerAndroid | |
.bind(this, { | |
date: this.state.currentDate, | |
mode: textInputDateMode, | |
minDate: textInputDateMinDate, | |
maxDate: textInputDateMaxDate | |
})}> | |
<View style={styles.datePickerStyle}> | |
<Text>{this._formatDate(this.state.currentDate, "date", textInputDateFormat)}</Text> | |
</View> | |
</TouchableWithoutFeedback> | |
</View> | |
) | |
} | |
{Platform.OS === 'android' && textInputDateTime && ( | |
<View style={textInputContainerStyle}> | |
<Text>Sorry not supported yet by react native | |
</Text> | |
</View> | |
) | |
} | |
{Platform.OS === 'android' && textInputTime && ( | |
<View style={textInputContainerStyle}> | |
<TouchableWithoutFeedback | |
onPress={this | |
.showTimePickerAndroid | |
.bind(this, { | |
hour: this.state.currentHour, | |
minute: this.state.currentMinute, | |
is24Hour: this.state.is24Hour | |
})}> | |
<View style={styles.datePickerStyle}> | |
<Text>{this.state.currentTime}</Text> | |
</View> | |
</TouchableWithoutFeedback> | |
</View> | |
) | |
} | |
{Platform.OS === 'ios' && textInputDate && ( | |
<View style={textInputContainerStyle}> | |
<TouchableWithoutFeedback> | |
<Modal | |
transparent={true} | |
animationType="none" | |
visible={this.state.modalVisible} | |
supportedOrientations={SUPPORTED_ORIENTATIONS} | |
onRequestClose={() => { this.setModalVisible(false); }} | |
> | |
<View | |
style={{ flex: 1 }} | |
> | |
<TouchableHighlight | |
style={Style.datePickerMask} | |
activeOpacity={1} | |
underlayColor={'#00000077'} | |
onPress={this.onPressCancel} | |
> | |
<TouchableHighlight | |
underlayColor={'#fff'} | |
style={{ flex: 1 }} | |
> | |
<Animated.View | |
style={[Style.datePickerCon, { height: this.state.animatedHeight }, customStyles.datePickerCon]} | |
> | |
<DatePickerIOS | |
date={this.state.date} | |
mode={mode} | |
minimumDate={minDate && this.getDate(minDate)} | |
maximumDate={maxDate && this.getDate(maxDate)} | |
onDateChange={(date) => this.setState({ date: date })} | |
minuteInterval={minuteInterval} | |
timeZoneOffsetInMinutes={timeZoneOffsetInMinutes} | |
style={[Style.datePicker, customStyles.datePicker]} | |
/> | |
<TouchableHighlight | |
underlayColor={'transparent'} | |
onPress={this.onPressCancel} | |
style={[Style.btnText, Style.btnCancel, customStyles.btnCancel]} | |
> | |
<Text | |
style={[Style.btnTextText, Style.btnTextCancel, customStyles.btnTextCancel]} | |
> | |
{cancelBtnText} | |
</Text> | |
</TouchableHighlight> | |
<TouchableHighlight | |
underlayColor={'transparent'} | |
onPress={this.onPressConfirm} | |
style={[Style.btnText, Style.btnConfirm, customStyles.btnConfirm]} | |
> | |
<Text style={[Style.btnTextText, customStyles.btnTextConfirm]}>{confirmBtnText}</Text> | |
</TouchableHighlight> | |
</Animated.View> | |
</TouchableHighlight> | |
</TouchableHighlight> | |
</View> | |
</Modal> | |
</TouchableWithoutFeedback> | |
</View> | |
) | |
} | |
{Platform.OS === 'ios' && textInputDateTime && ( | |
<View> | |
</View> | |
) | |
} | |
{Platform.OS === 'ios' && textInputTime && ( | |
<View> | |
</View> | |
) | |
} | |
{!hideChevron && ( | |
<View style={styles.chevronContainer} onPress={chevronOnPress}> | |
<Icon | |
type={rightIcon.type} | |
iconStyle={rightIcon.style} | |
size={28} | |
name={rightIcon.name || 'chevron-right'} | |
color={rightIcon.color || chevronColor} /> | |
</View> | |
) | |
} | |
{switchButton && hideChevron && ( | |
<View style={styles.switchContainer}> | |
<Switch | |
onValueChange={this | |
._onSwitchChange | |
.bind(this)} | |
style={{ | |
marginBottom: 10 | |
}} | |
disabled={switchDisabled} | |
onTintColor={switchOnTintColor} | |
thumbTintColor={switchThumbTintColor} | |
tintColor={switchTintColor} | |
value={switched} /> | |
</View> | |
) | |
} | |
{badge && !rightTitle && (<Badge badge={badge} />) | |
} | |
{label && label | |
} | |
</View> | |
</TouchableHighlight> | |
) | |
} | |
}; | |
ListItem.defaultProps = { | |
underlayColor: 'white', | |
chevronColor: colors.grey4, | |
rightIcon: { | |
name: 'chevron-right' | |
}, | |
hideChevron: false, | |
roundAvatar: false, | |
textInputEditable: true | |
} | |
ListItem.propTypes = { | |
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]), | |
avatar: PropTypes.object, | |
icon: PropTypes.any, | |
onPress: PropTypes.func, | |
rightIcon: PropTypes.object, | |
underlayColor: PropTypes.string, | |
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]), | |
subtitleStyle: PropTypes.any, | |
containerStyle: PropTypes.any, | |
wrapperStyle: PropTypes.any, | |
titleStyle: PropTypes.any, | |
titleContainerStyle: PropTypes.any, | |
hideChevron: PropTypes.bool, | |
chevronColor: PropTypes.string, | |
roundAvatar: PropTypes.bool, | |
badge: PropTypes.any, | |
switchButton: PropTypes.bool, | |
onSwitch: PropTypes.func, | |
switchDisabled: PropTypes.bool, | |
switchOnTintColor: PropTypes.string, | |
switchThumbTintColor: PropTypes.string, | |
switchTintColor: PropTypes.string, | |
switched: PropTypes.bool, | |
textInput: PropTypes.bool, | |
textInputAutoCapitalize: PropTypes.bool, | |
textInputAutoCorrect: PropTypes.bool, | |
textInputAutoFocus: PropTypes.bool, | |
textInputEditable: PropTypes.bool, | |
textInputKeyboardType: PropTypes.oneOf([ | |
'default', | |
'email-address', | |
'numeric', | |
'phone-pad', | |
'ascii-capable', | |
'numbers-and-punctuation', | |
'url', | |
'number-pad', | |
'name-phone-pad', | |
'decimal-pad', | |
'twitter', | |
'web-search' | |
]), | |
textInputMaxLength: PropTypes.number, | |
textInputMultiline: PropTypes.bool, | |
textInputOnChangeText: PropTypes.func, | |
textInputOnFocus: PropTypes.func, | |
textInputOnBlur: PropTypes.func, | |
textInputSelectTextOnFocus: PropTypes.bool, | |
textInputReturnKeyType: PropTypes.string, | |
textInputValue: PropTypes.string, | |
textInputStyle: PropTypes.any, | |
textInputContainerStyle: PropTypes.any, | |
textInputDate: PropTypes.any, | |
textInputDateMinDate: PropTypes.any, | |
textInputDateMaxDate: PropTypes.any, | |
textInputDateTime: PropTypes.any, | |
textInputTime: PropTypes.any, | |
textInputHour: PropTypes.number, | |
textInputMinute: PropTypes.number, | |
textInputHourFormat: PropTypes.string, | |
textInputMinuteFormat: PropTypes.string, | |
textInputTimeIs24Hour: PropTypes.bool, | |
textInputDateOnChange: PropTypes.func, | |
textInputDateMode: PropTypes.oneOf(['default', 'calendar', 'spinner', 'default']), | |
textInputDateIosMode: PropTypes.oneOf(['date', 'time', 'datetime']), | |
switchOnChange: PropTypes.func, | |
chevronOnPress: PropTypes.func | |
}; | |
styles = StyleSheet.create({ | |
avatar: { | |
width: 34, | |
height: 34 | |
}, | |
container: { | |
paddingTop: 10, | |
paddingRight: 10, | |
paddingBottom: 10, | |
borderBottomColor: '#ededed', | |
borderBottomWidth: 1, | |
backgroundColor: 'transparent' | |
}, | |
wrapper: { | |
flexDirection: 'row', | |
marginLeft: 10 | |
}, | |
iconStyle: { | |
flex: 0.15, | |
justifyContent: 'center', | |
alignItems: 'center' | |
}, | |
icon: { | |
marginRight: 8 | |
}, | |
title: { | |
fontSize: normalize(14), | |
color: colors.grey1 | |
}, | |
subtitle: { | |
color: colors.grey3, | |
fontSize: normalize(12), | |
marginTop: 1, | |
...Platform.select({ | |
ios: { | |
fontWeight: '600' | |
}, | |
android: { | |
...fonts.android.bold | |
} | |
}) | |
}, | |
titleSubtitleContainer: { | |
justifyContent: 'center', | |
flex: 1 | |
}, | |
chevronContainer: { | |
flex: 0.3, | |
alignItems: 'flex-end', | |
justifyContent: 'center' | |
}, | |
switchContainer: { | |
flex: 0.15, | |
alignItems: 'flex-end', | |
justifyContent: 'center', | |
marginRight: 5 | |
}, | |
rightTitleContainer: { | |
flex: 1, | |
alignItems: 'flex-end', | |
justifyContent: 'center' | |
}, | |
rightTitleStyle: { | |
marginRight: 5, | |
color: colors.grey4 | |
}, | |
datePickerStyle: { | |
alignItems: 'flex-start', | |
flex: 1 | |
} | |
}); | |
export default ListItem; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment