diff --git a/components/app-text-input.js b/components/app-text-input.js new file mode 100644 index 0000000000000000000000000000000000000000..8c9d27332dc8febc293bf92961011a853132332c --- /dev/null +++ b/components/app-text-input.js @@ -0,0 +1,25 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { TextInput } from 'react-native' +import styles from '../styles' + +export default function AppTextInput({ style, ...props }) { + return ( + <TextInput + style={[styles.textInputField, ...style]} + autoFocus={props.autoFocus} + onChangeText={props.onChangeText} + value={props.value} + placeholder={props.placeholder} + {...props} + /> + ) +} + +AppTextInput.propTypes = { + secureTextEntry: PropTypes.bool +} + +AppTextInput.defaultProps = { + style: [] +} diff --git a/components/cycle-day/symptoms/symptom-section.js b/components/cycle-day/symptoms/symptom-section.js index 90550621e2c3e5894f6172422dd40392352a120b..477ba8645ebf7ef746d56773353a1b4ffe999018 100644 --- a/components/cycle-day/symptoms/symptom-section.js +++ b/components/cycle-day/symptoms/symptom-section.js @@ -20,9 +20,11 @@ export default class SymptomSection extends Component { flex={1} alignItems={p.inline ? 'center' : null} > - <View flex={1}> - <AppText>{p.explainer}</AppText> - </View> + { p.explainer && ( + <View flex={1}> + <AppText>{p.explainer}</AppText> + </View> + )} {p.children} </View> </View> diff --git a/components/cycle-day/symptoms/temperature.js b/components/cycle-day/symptoms/temperature.js index ae4634ca83415045d827c7888d99827dee75214f..228491b4fb13789e5bccb5336ffa8c1219e55920 100644 --- a/components/cycle-day/symptoms/temperature.js +++ b/components/cycle-day/symptoms/temperature.js @@ -1,7 +1,6 @@ import React, { Component } from 'react' import { View, - TextInput, Switch, Keyboard, Alert, @@ -18,6 +17,8 @@ import { scaleObservable } from '../../../local-storage' import { shared as sharedLabels } from '../../../i18n/en/labels' import ActionButtonFooter from './action-button-footer' import config from '../../../config' +import AppTextInput from '../../app-text-input' +import AppText from '../../app-text' import SymptomSection from './symptom-section' const minutes = ChronoUnit.MINUTES @@ -27,7 +28,6 @@ export default class Temp extends Component { super(props) const cycleDay = props.cycleDay this.temperature = cycleDay && cycleDay.temperature - this.makeActionButtons = props.makeActionButtons const temp = this.temperature @@ -66,14 +66,12 @@ export default class Temp extends Component { checkRangeAndSave = () => { const value = Number(this.state.temperature) - - const absolute = { - min: config.temperatureScale.min, - max: config.temperatureScale.max - } + const { min, max } = config.temperatureScale + const range = { min, max } const scale = scaleObservable.value let warningMsg - if (value < absolute.min || value > absolute.max) { + + if (value < range.min || value > range.max) { warningMsg = labels.outOfAbsoluteRangeWarning } else if (value < scale.min || value > scale.max) { warningMsg = labels.outOfRangeWarning @@ -91,36 +89,54 @@ export default class Temp extends Component { } else { this.saveTemperature() } + } + + setTemperature = (temperature) => { + if (isNaN(Number(temperature))) return + this.setState({ temperature, isSuggestion: false }) + } + setNote = (note) => { + this.setState({ note }) } + showTimePicker = () => { + Keyboard.dismiss() + this.setState({ isTimePickerVisible: true }) + } render() { + const inputStyle = [styles.temperatureTextInput] + if (this.state.isSuggestion) { + inputStyle.push(styles.temperatureTextInputSuggestion) + } return ( <View style={{ flex: 1 }}> <ScrollView style={styles.page}> - <View> - <SymptomSection - header={labels.temperature.header} - explainer={labels.temperature.explainer} - inline={true} - > - <TempInput + <SymptomSection + header={labels.temperature.header} + explainer={labels.temperature.explainer} + > + <View style={styles.framedSegmentInlineChildren}> + <AppTextInput + style={[inputStyle]} + autoFocus={true} + placeholder={this.state.temperature} value={this.state.temperature} - setState={(val) => this.setState(val)} - isSuggestion={this.state.isSuggestion} + onChangeText={this.setTemperature} + keyboardType='numeric' + onBlur={this.checkRange} /> - </SymptomSection> - <SymptomSection - header={labels.time} - inline={true} - > - <TextInput - style={styles.temperatureTextInput} - onFocus={() => { - Keyboard.dismiss() - this.setState({ isTimePickerVisible: true }) - }} + <AppText style={{ marginLeft: 5 }}>°C</AppText> + </View> + </SymptomSection> + <SymptomSection + header={labels.time} + > + <View style={styles.framedSegmentInlineChildren}> + <AppTextInput + style={[styles.temperatureTextInput]} + onFocus={this.showTimePicker} value={this.state.time} /> <DateTimePicker @@ -134,34 +150,32 @@ export default class Temp extends Component { }} onCancel={() => this.setState({ isTimePickerVisible: false })} /> - </SymptomSection> - <SymptomSection - header={labels.note.header} - explainer={labels.note.explainer} - > - <TextInput - multiline={true} - autoFocus={this.state.focusTextArea} - placeholder={sharedLabels.enter} - value={this.state.note} - onChangeText={(val) => { - this.setState({ note: val }) - }} - /> - </SymptomSection> - <SymptomSection - header={labels.exclude.header} - explainer={labels.exclude.explainer} - inline={true} - > - <Switch - onValueChange={(val) => { - this.setState({ exclude: val }) - }} - value={this.state.exclude} - /> - </SymptomSection> - </View> + </View> + </SymptomSection> + <SymptomSection + header={labels.note.header} + explainer={labels.note.explainer} + > + <AppTextInput + multiline={true} + autoFocus={this.state.focusTextArea} + placeholder={sharedLabels.enter} + value={this.state.note} + onChangeText={this.setNote} + /> + </SymptomSection> + <SymptomSection + header={labels.exclude.header} + explainer={labels.exclude.explainer} + inline={true} + > + <Switch + onValueChange={(val) => { + this.setState({ exclude: val }) + }} + value={this.state.exclude} + /> + </SymptomSection> </ScrollView> <ActionButtonFooter symptom='temperature' @@ -181,28 +195,6 @@ export default class Temp extends Component { } } -class TempInput extends Component { - render() { - const style = [styles.temperatureTextInput] - if (this.props.isSuggestion) { - style.push(styles.temperatureTextInputSuggestion) - } - return ( - <TextInput - style={style} - onChangeText={(val) => { - if (isNaN(Number(val))) return - this.props.setState({ temperature: val, isSuggestion: false }) - }} - keyboardType='numeric' - value={this.props.value} - onBlur={this.checkRange} - autoFocus={true} - /> - ) - } -} - function isInvalidTime(timeString) { try { LocalTime.parse(timeString) diff --git a/components/framed-segment.js b/components/framed-segment.js index 5d2c68fc73809a041dad945f3a60066622578180..800c564749646efb2831509f6e3c3df93d73d508 100644 --- a/components/framed-segment.js +++ b/components/framed-segment.js @@ -5,7 +5,7 @@ import { View } from 'react-native' import AppText from './app-text' import styles from '../styles' -const FramedSegment = ({ children, ...props }) => { +const FramedSegment = ({children, ...props}) => { const style = [styles.framedSegment, props.style] if (props.last) style.push(styles.framedSegmentLast) return ( diff --git a/components/settings/shared/password-field.js b/components/settings/shared/password-field.js index a62b4354946287d845332967998761aebc0da9a0..7937b6696a23bb2bce05691bd1fba37e145afc54 100644 --- a/components/settings/shared/password-field.js +++ b/components/settings/shared/password-field.js @@ -1,19 +1,22 @@ import React from 'react' -import { TextInput } from 'react-native' -import styles, {secondaryColor} from '../../../styles' +import PropTypes from 'prop-types' +import AppTextInput from '../../app-text-input' + +import styles from '../../../styles' export default function PasswordField(props) { return ( - <TextInput - style={styles.passwordField} - autoFocus={props.autoFocus === false ? false : true} - secureTextEntry={true} - onChangeText={props.onChangeText} - value={props.value} - placeholder={props.placeholder} - borderWidth={1} - borderColor={secondaryColor} - borderStyle={'solid'} + <AppTextInput + style={ styles.passwordField } + secureTextEntry + {...props} /> ) } + +PasswordField.propTypes = { + placeholder: PropTypes.string, + value: PropTypes.string, + onChangeText: PropTypes.func, + autoFocus: PropTypes.bool +} diff --git a/i18n/en/cycle-day.js b/i18n/en/cycle-day.js index 79b8a0e3c9e2e832ed468da2442db45bb1b35819..f399634e1e2cce5edc364fcdfd2a9e847f7839cd 100644 --- a/i18n/en/cycle-day.js +++ b/i18n/en/cycle-day.js @@ -107,7 +107,7 @@ export const temperature = { outOfAbsoluteRangeWarning: 'This temperature value is too high or low to be shown on the temperature chart.', saveAnyway: 'Save anyway', temperature: { - header: "Temperature (°C)", + header: "Temperature", explainer: 'Take your temperature right after waking up, before getting out of bed' }, time: "Time", diff --git a/styles/index.js b/styles/index.js index 00ebca0f495b7a935b87bff8c78d91a4130f470b..2ef7ebeb43643f2e1e6f9003aa52406721223df2 100644 --- a/styles/index.js +++ b/styles/index.js @@ -269,7 +269,8 @@ export default StyleSheet.create({ temperatureTextInput: { fontSize: 20, color: 'black', - textAlign: 'center' + textAlign: 'center', + width: '30%' }, temperatureTextInputSuggestion: { color: '#939393' @@ -299,6 +300,10 @@ export default StyleSheet.create({ fontWeight: 'bold', fontFamily: textFontBold }, + framedSegmentInlineChildren: { + flexDirection: 'row', + alignItems: 'center' + }, settingsButton: { padding: 10, alignItems: 'center', @@ -383,10 +388,16 @@ export default StyleSheet.create({ marginTop: 1 }, passwordField: { - padding: 10, - marginTop: 10, marginHorizontal: 10, - backgroundColor: 'white' + marginTop: 10 + }, + textInputField: { + padding: 10, + marginVertical: 10, + backgroundColor: 'white', + borderColor: secondaryColor, + borderStyle: 'solid', + borderWidth: 1, }, passwordPromptPage: { padding: 30,