Separates TemperatureSlider component, adds minor post-review changes
This commit is contained in:
@@ -5,15 +5,16 @@ import Icon from 'react-native-vector-icons/Entypo'
|
|||||||
|
|
||||||
import { Sizes } from '../../styles/redesign'
|
import { Sizes } from '../../styles/redesign'
|
||||||
|
|
||||||
const AppIcon = ({ color, name }) => {
|
const AppIcon = ({ color, name, style, ...props }) => {
|
||||||
const style = [styles.icon, { color }]
|
const iconStyle = [styles.icon, style, { color }]
|
||||||
|
|
||||||
return <Icon name={name} style={style}/>
|
return <Icon name={name} style={iconStyle} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
AppIcon.propTypes = {
|
AppIcon.propTypes = {
|
||||||
color: PropTypes.string,
|
color: PropTypes.string,
|
||||||
name: PropTypes.string.isRequired
|
name: PropTypes.string.isRequired,
|
||||||
|
style: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
AppIcon.defaultProps = {
|
AppIcon.defaultProps = {
|
||||||
|
|||||||
@@ -3,21 +3,15 @@ import PropTypes from 'prop-types'
|
|||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
import AppText from './app-text'
|
import AppText from './app-text'
|
||||||
import AppIcon from './app-icon'
|
|
||||||
|
|
||||||
import { Colors, Containers, Spacing, Typography } from '../../styles/redesign'
|
import { Colors, Spacing, Typography } from '../../styles/redesign'
|
||||||
|
|
||||||
const Segment = ({ children, icon, last, title }) => {
|
const Segment = ({ children, last, title }) => {
|
||||||
const containerStyle = last ? styles.containerLast : styles.container
|
const containerStyle = last ? styles.containerLast : styles.container
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={containerStyle}>
|
<View style={containerStyle}>
|
||||||
{title &&
|
{title && <AppText style={styles.title}>{title}</AppText>}
|
||||||
<View style={styles.line}>
|
|
||||||
{icon && <AppIcon name={icon} color={Colors.purple} />}
|
|
||||||
<AppText style={styles.title}>{title}</AppText>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -25,7 +19,6 @@ const Segment = ({ children, icon, last, title }) => {
|
|||||||
|
|
||||||
Segment.propTypes = {
|
Segment.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
icon: PropTypes.string,
|
|
||||||
last: PropTypes.bool,
|
last: PropTypes.bool,
|
||||||
title: PropTypes.string
|
title: PropTypes.string
|
||||||
}
|
}
|
||||||
@@ -46,13 +39,9 @@ const styles = StyleSheet.create({
|
|||||||
containerLast: {
|
containerLast: {
|
||||||
...segmentContainer
|
...segmentContainer
|
||||||
},
|
},
|
||||||
line: {
|
|
||||||
alignItems: 'center',
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
...Typography.subtitle
|
...Typography.subtitle
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default Segment
|
export default Segment
|
||||||
@@ -1,87 +1,58 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import Slider from '@ptomasroos/react-native-multi-slider'
|
|
||||||
|
|
||||||
import alertError from '../shared/alert-error'
|
import AppIcon from '../../common/app-icon'
|
||||||
import AppPage from '../../common/app-page'
|
import AppPage from '../../common/app-page'
|
||||||
import AppSwitch from '../../common/app-switch'
|
import AppSwitch from '../../common/app-switch'
|
||||||
import AppText from '../../common/app-text'
|
import AppText from '../../common/app-text'
|
||||||
import Label from './label'
|
import TemperatureSlider from './temperature-slider'
|
||||||
import Segment from '../../common/segment'
|
import Segment from '../../common/segment'
|
||||||
|
|
||||||
import { useCervixObservable,
|
import { useCervixObservable, saveUseCervix } from '../../../local-storage'
|
||||||
saveUseCervix,
|
import { Colors, Spacing, Typography } from '../../../styles/redesign'
|
||||||
scaleObservable,
|
|
||||||
saveTempScale
|
|
||||||
} from '../../../local-storage'
|
|
||||||
import { Colors, Sizes } from '../../../styles/redesign'
|
|
||||||
import labels from '../../../i18n/en/settings'
|
import labels from '../../../i18n/en/settings'
|
||||||
import config from '../../../config'
|
|
||||||
|
|
||||||
export default class Settings extends Component {
|
export default class Settings extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
useCervix: useCervixObservable.value,
|
shouldUseCervix: useCervixObservable.value
|
||||||
temperatureScale: { ...scaleObservable.value }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCervixToggle = (value) => {
|
onCervixToggle = (value) => {
|
||||||
this.setState({ useCervix: value })
|
this.setState({ shouldUseCervix: value })
|
||||||
saveUseCervix(value)
|
saveUseCervix(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSliderChange = (values) => {
|
|
||||||
this.setState({ min: values[0], max: values[1] })
|
|
||||||
}
|
|
||||||
|
|
||||||
onSliderChangeFinish = (values) => {
|
|
||||||
this.setState({ min: values[0], max: values[1] })
|
|
||||||
|
|
||||||
try {
|
|
||||||
saveTempScale({ min: values[0], max: values[1] })
|
|
||||||
} catch(err) {
|
|
||||||
alertError(labels.tempScale.saveError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { useCervix } = this.state
|
const { shouldUseCervix } = this.state
|
||||||
const cervixText = useCervix ?
|
const cervixText = shouldUseCervix ?
|
||||||
labels.useCervix.cervixModeOn : labels.useCervix.cervixModeOff
|
labels.useCervix.cervixModeOn : labels.useCervix.cervixModeOff
|
||||||
const { min, max } = this.state.temperatureScale
|
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<AppPage>
|
||||||
<Segment title={labels.useCervix.title}>
|
<Segment title={labels.useCervix.title}>
|
||||||
<AppSwitch
|
<AppSwitch
|
||||||
onToggle={this.onCervixToggle}
|
onToggle={this.onCervixToggle}
|
||||||
text={cervixText}
|
text={cervixText}
|
||||||
value={useCervix}
|
value={shouldUseCervix}
|
||||||
/>
|
/>
|
||||||
</Segment>
|
</Segment>
|
||||||
<Segment title={labels.tempScale.segmentTitle}>
|
<Segment title={labels.tempScale.segmentTitle}>
|
||||||
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
<AppText>{labels.tempScale.segmentExplainer}</AppText>
|
||||||
<View style={styles.container}>
|
<TemperatureSlider />
|
||||||
<Slider
|
|
||||||
customLabel={Label}
|
|
||||||
enableLabel={true}
|
|
||||||
markerStyle={styles.marker}
|
|
||||||
markerOffsetY={Sizes.tiny}
|
|
||||||
max={config.temperatureScale.max}
|
|
||||||
min={config.temperatureScale.min}
|
|
||||||
onValuesChange={this.onSliderChange}
|
|
||||||
onValuesChangeFinish={this.onSliderChangeFinish}
|
|
||||||
selectedStyle={styles.sliderAccentBackground}
|
|
||||||
step={config.temperatureScale.step}
|
|
||||||
trackStyle={styles.slider}
|
|
||||||
unselectedStyle={styles.sliderBackground}
|
|
||||||
values={[min, max]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</Segment>
|
</Segment>
|
||||||
<Segment icon="info-with-circle" last title={labels.preOvu.title}>
|
<Segment last>
|
||||||
|
<View style={styles.line}>
|
||||||
|
<AppIcon
|
||||||
|
color={Colors.purple}
|
||||||
|
name="info-with-circle"
|
||||||
|
style={styles.icon}
|
||||||
|
/>
|
||||||
|
<AppText style={styles.title}>{labels.preOvu.title}</AppText>
|
||||||
|
</View>
|
||||||
<AppText>{labels.preOvu.note}</AppText>
|
<AppText>{labels.preOvu.note}</AppText>
|
||||||
</Segment>
|
</Segment>
|
||||||
</AppPage>
|
</AppPage>
|
||||||
@@ -90,25 +61,14 @@ export default class Settings extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
icon: {
|
||||||
alignItems: 'center',
|
marginRight: Spacing.base
|
||||||
paddingTop: Sizes.base
|
|
||||||
},
|
},
|
||||||
marker: {
|
line: {
|
||||||
backgroundColor: Colors.tourquiseDark,
|
flexDirection: 'row',
|
||||||
borderRadius: 50,
|
alignItems: 'center'
|
||||||
elevation: 4,
|
|
||||||
height: Sizes.subtitle,
|
|
||||||
width: Sizes.subtitle
|
|
||||||
},
|
|
||||||
slider: {
|
|
||||||
borderRadius: 25,
|
|
||||||
height: Sizes.small
|
|
||||||
},
|
|
||||||
sliderAccentBackground: {
|
|
||||||
backgroundColor: Colors.tourquiseDark
|
|
||||||
},
|
|
||||||
sliderBackground: {
|
|
||||||
backgroundColor: Colors.tourquise
|
|
||||||
},
|
},
|
||||||
|
title: {
|
||||||
|
...Typography.subtitle
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { StyleSheet } from 'react-native'
|
|
||||||
|
|
||||||
import AppText from '../../common/app-text'
|
|
||||||
|
|
||||||
import { Sizes } from '../../../styles/redesign'
|
|
||||||
|
|
||||||
const sliderRadius = 5
|
|
||||||
const width = 50
|
|
||||||
|
|
||||||
export default class Label extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
oneMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
twoMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
oneMarkerLeftPosition: PropTypes.number,
|
|
||||||
twoMarkerLeftPosition: PropTypes.number,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
oneMarkerValue,
|
|
||||||
twoMarkerValue,
|
|
||||||
oneMarkerLeftPosition,
|
|
||||||
twoMarkerLeftPosition,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
const minCoordinate = oneMarkerLeftPosition - width / 2 + sliderRadius
|
|
||||||
const maxCoordinate = twoMarkerLeftPosition - width / 2 + sliderRadius
|
|
||||||
const isMinNumber = Number.isFinite(oneMarkerLeftPosition) &&
|
|
||||||
Number.isFinite(oneMarkerValue)
|
|
||||||
const isMaxNumber = Number.isFinite(twoMarkerLeftPosition) &&
|
|
||||||
Number.isFinite(twoMarkerValue)
|
|
||||||
const minStyle = [styles.label, { left: minCoordinate }]
|
|
||||||
const maxStyle = [styles.label, { left: maxCoordinate }]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{isMinNumber && <AppText style={minStyle}>{oneMarkerValue}</AppText>}
|
|
||||||
{isMaxNumber && <AppText style={maxStyle}>{twoMarkerValue}</AppText>}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
label: {
|
|
||||||
position: 'absolute',
|
|
||||||
marginTop: (-1) * Sizes.base
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { StyleSheet } from 'react-native'
|
||||||
|
|
||||||
|
import AppText from '../../common/app-text'
|
||||||
|
|
||||||
|
import { Fonts, Sizes } from '../../../styles/redesign'
|
||||||
|
|
||||||
|
const sliderRadius = 5
|
||||||
|
const width = 50
|
||||||
|
|
||||||
|
const getMarkerCoordinate = (position) => {
|
||||||
|
return position - width / 2 + sliderRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
const SliderLabel = ({
|
||||||
|
oneMarkerValue,
|
||||||
|
twoMarkerValue,
|
||||||
|
oneMarkerLeftPosition,
|
||||||
|
twoMarkerLeftPosition
|
||||||
|
}) => {
|
||||||
|
const minCoordinate = getMarkerCoordinate(oneMarkerLeftPosition)
|
||||||
|
const maxCoordinate = getMarkerCoordinate(twoMarkerLeftPosition)
|
||||||
|
const isMinNumber = Number.isFinite(oneMarkerLeftPosition) &&
|
||||||
|
Number.isFinite(oneMarkerValue)
|
||||||
|
const isMaxNumber = Number.isFinite(twoMarkerLeftPosition) &&
|
||||||
|
Number.isFinite(twoMarkerValue)
|
||||||
|
const minStyle = [styles.label, { left: minCoordinate }]
|
||||||
|
const maxStyle = [styles.label, { left: maxCoordinate }]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{isMinNumber && <AppText style={minStyle}>{oneMarkerValue}</AppText>}
|
||||||
|
{isMaxNumber && <AppText style={maxStyle}>{twoMarkerValue}</AppText>}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SliderLabel.propTypes = {
|
||||||
|
oneMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
twoMarkerValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
oneMarkerLeftPosition: PropTypes.number,
|
||||||
|
twoMarkerLeftPosition: PropTypes.number
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
label: {
|
||||||
|
fontFamily: Fonts.bold,
|
||||||
|
position: 'absolute',
|
||||||
|
marginTop: (-1) * Sizes.base
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default SliderLabel
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { StyleSheet, View } from 'react-native'
|
||||||
|
import Slider from '@ptomasroos/react-native-multi-slider'
|
||||||
|
|
||||||
|
import alertError from '../shared/alert-error'
|
||||||
|
import SliderLabel from './slider-label'
|
||||||
|
|
||||||
|
import { scaleObservable, saveTempScale } from '../../../local-storage'
|
||||||
|
import { Colors, Sizes } from '../../../styles/redesign'
|
||||||
|
import labels from '../../../i18n/en/settings'
|
||||||
|
import config from '../../../config'
|
||||||
|
|
||||||
|
export default class TemperatureSlider extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
const { min, max } = scaleObservable.value
|
||||||
|
this.state = { minTemperature: min, maxTemperature: max }
|
||||||
|
}
|
||||||
|
|
||||||
|
onTemperatureSliderChange = (values) => {
|
||||||
|
this.setState({
|
||||||
|
minTemperature: values[0],
|
||||||
|
maxTemperature: values[1]
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveTempScale({ min: values[0], max: values[1] })
|
||||||
|
} catch(err) {
|
||||||
|
alertError(labels.tempScale.saveError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { minTemperature, maxTemperature } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Slider
|
||||||
|
customLabel={SliderLabel}
|
||||||
|
enableLabel={true}
|
||||||
|
markerStyle={styles.marker}
|
||||||
|
markerOffsetY={Sizes.tiny}
|
||||||
|
max={config.temperatureScale.max}
|
||||||
|
min={config.temperatureScale.min}
|
||||||
|
onValuesChange={this.onTemperatureSliderChange}
|
||||||
|
selectedStyle={styles.sliderAccentBackground}
|
||||||
|
step={config.temperatureScale.step}
|
||||||
|
trackStyle={styles.slider}
|
||||||
|
unselectedStyle={styles.sliderBackground}
|
||||||
|
values={[minTemperature, maxTemperature]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: Sizes.base
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
backgroundColor: Colors.tourquiseDark,
|
||||||
|
borderRadius: 50,
|
||||||
|
elevation: 4,
|
||||||
|
height: Sizes.subtitle,
|
||||||
|
width: Sizes.subtitle
|
||||||
|
},
|
||||||
|
slider: {
|
||||||
|
borderRadius: 25,
|
||||||
|
height: Sizes.small
|
||||||
|
},
|
||||||
|
sliderAccentBackground: {
|
||||||
|
backgroundColor: Colors.tourquiseDark
|
||||||
|
},
|
||||||
|
sliderBackground: {
|
||||||
|
backgroundColor: Colors.tourquise
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user