Merge branch 'chart-refactoring-part-1' into 'master'
Chart refactoring part 1 See merge request bloodyhealth/drip!254
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
|
import AppText from '../app-text'
|
||||||
|
import DripHomeIcon from '../../assets/drip-home-icons'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
import { cycleDayColor } from '../../styles'
|
||||||
|
|
||||||
|
import { shared as labels } from '../../i18n/en/labels'
|
||||||
|
|
||||||
|
const ChartLegend = () => {
|
||||||
|
return (
|
||||||
|
<View style={[styles.yAxis, styles.chartLegend]}>
|
||||||
|
<DripHomeIcon
|
||||||
|
name="circle"
|
||||||
|
size={styles.yAxis.width - 7}
|
||||||
|
color={cycleDayColor}
|
||||||
|
/>
|
||||||
|
<AppText style={styles.yAxisLabels.dateLabel}>
|
||||||
|
{labels.date.toLowerCase()}
|
||||||
|
</AppText>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartLegend
|
||||||
+22
-52
@@ -1,31 +1,20 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { View, FlatList, ActivityIndicator } from 'react-native'
|
import { View, FlatList, ActivityIndicator } from 'react-native'
|
||||||
import { LocalDate } from 'js-joda'
|
import { LocalDate } from 'js-joda'
|
||||||
import { makeYAxisLabels, makeHorizontalGrid } from './y-axis'
|
|
||||||
|
import YAxis from './y-axis'
|
||||||
import nfpLines from './nfp-lines'
|
import nfpLines from './nfp-lines'
|
||||||
import DayColumn from './day-column'
|
import DayColumn from './day-column'
|
||||||
|
import HorizontalGrid from './horizontal-grid'
|
||||||
|
|
||||||
import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
|
import { getCycleDaysSortedByDate, getAmountOfCycleDays } from '../../db'
|
||||||
import styles from './styles'
|
import styles from './styles'
|
||||||
import { cycleDayColor } from '../../styles'
|
|
||||||
import { scaleObservable } from '../../local-storage'
|
import { scaleObservable } from '../../local-storage'
|
||||||
import config from '../../config'
|
import config from '../../config'
|
||||||
import AppText from '../app-text'
|
|
||||||
import AppLoadingView from '../app-loading'
|
|
||||||
import { shared as labels } from '../../i18n/en/labels'
|
|
||||||
import DripIcon from '../../assets/drip-icons'
|
|
||||||
import DripHomeIcon from '../../assets/drip-home-icons'
|
|
||||||
import nothingChanged from '../../db/db-unchanged'
|
|
||||||
|
|
||||||
const symptomIcons = {
|
import AppLoadingView from '../app-loading'
|
||||||
bleeding: <DripIcon size={16} name='drip-icon-bleeding' color={styles.iconShades.bleeding[3]}/>,
|
|
||||||
mucus: <DripIcon size={16} name='drip-icon-mucus' color={styles.iconShades.mucus[4]}/>,
|
import nothingChanged from '../../db/db-unchanged'
|
||||||
cervix: <DripIcon size={16} name='drip-icon-cervix' color={styles.iconShades.cervix[3]}/>,
|
|
||||||
desire: <DripIcon size={16} name='drip-icon-desire' color={styles.iconShades.desire[2]}/>,
|
|
||||||
sex: <DripIcon size={16} name='drip-icon-sex' color={styles.iconShades.sex[2]}/>,
|
|
||||||
pain: <DripIcon size={16} name='drip-icon-pain' color={styles.iconShades.pain[0]}/>,
|
|
||||||
mood: <DripIcon size={16} name='drip-icon-mood' color={styles.iconShades.mood[0]}/>,
|
|
||||||
note: <DripIcon size={16} name='drip-icon-note' color={styles.iconShades.note[0]}/>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class CycleChart extends Component {
|
export default class CycleChart extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -129,49 +118,30 @@ export default class CycleChart extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { chartHeight, chartLoaded } = this.state
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
onLayout={this.onLayout}
|
onLayout={this.onLayout}
|
||||||
style={{ flexDirection: 'row', flex: 1 }}
|
style={{ flexDirection: 'row', flex: 1 }}
|
||||||
>
|
>
|
||||||
{!this.state.chartLoaded && <AppLoadingView />}
|
{!chartLoaded && <AppLoadingView />}
|
||||||
|
|
||||||
{this.state.chartHeight && this.state.chartLoaded &&
|
{chartHeight && chartLoaded && (
|
||||||
<View>
|
<YAxis
|
||||||
<View style={[styles.yAxis, {height: this.symptomRowHeight}]}>
|
height={this.columnHeight}
|
||||||
{this.symptomRowSymptoms.map(symptomName => {
|
symptomsToDisplay={this.symptomRowSymptoms}
|
||||||
return <View
|
symptomsSectionHeight={this.symptomRowHeight}
|
||||||
style={{ alignItems: 'center', justifyContent: 'center' }}
|
/>
|
||||||
key={symptomName}
|
)}
|
||||||
width={styles.yAxis.width}
|
|
||||||
height={this.symptomRowHeight /
|
|
||||||
this.symptomRowSymptoms.length}
|
|
||||||
>
|
|
||||||
{symptomIcons[symptomName]}
|
|
||||||
</View>
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
<View style={[styles.yAxis, {height: this.columnHeight}]}>
|
|
||||||
{makeYAxisLabels(this.columnHeight)}
|
|
||||||
</View>
|
|
||||||
<View style={[styles.yAxis, { alignItems: 'center', justifyContent: 'center' }]}>
|
|
||||||
<DripHomeIcon
|
|
||||||
name="circle"
|
|
||||||
size={styles.yAxis.width - 7}
|
|
||||||
color={cycleDayColor}
|
|
||||||
/>
|
|
||||||
<AppText style={[styles.yAxisLabels.dateLabel]}>
|
|
||||||
{labels.date.toLowerCase()}
|
|
||||||
</AppText>
|
|
||||||
</View>
|
|
||||||
</View>}
|
|
||||||
|
|
||||||
|
{chartHeight && chartLoaded && (
|
||||||
{this.state.chartHeight && this.state.chartLoaded &&
|
<HorizontalGrid
|
||||||
makeHorizontalGrid(this.columnHeight, this.symptomRowHeight)
|
height={this.columnHeight}
|
||||||
|
startPosition={this.symptomRowHeight}
|
||||||
|
/>)
|
||||||
}
|
}
|
||||||
|
|
||||||
{this.state.chartHeight &&
|
{chartHeight &&
|
||||||
<FlatList
|
<FlatList
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
inverted={true}
|
inverted={true}
|
||||||
|
|||||||
@@ -18,8 +18,11 @@ import styles from './styles'
|
|||||||
import config from '../../config'
|
import config from '../../config'
|
||||||
import cycleModule from '../../lib/cycle'
|
import cycleModule from '../../lib/cycle'
|
||||||
import { getCycleDay } from '../../db'
|
import { getCycleDay } from '../../db'
|
||||||
|
|
||||||
import DotAndLine from './dot-and-line'
|
import DotAndLine from './dot-and-line'
|
||||||
import { normalizeToScale } from './y-axis'
|
import SymptomCell from './symptom-cell'
|
||||||
|
|
||||||
|
import { normalizeToScale } from '../helpers/chart'
|
||||||
|
|
||||||
const label = styles.column.label
|
const label = styles.column.label
|
||||||
|
|
||||||
@@ -139,41 +142,6 @@ class DayColumn extends Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
drawSymptom = (symptom) => {
|
|
||||||
|
|
||||||
const { symptomHeight } = this.props
|
|
||||||
const shouldDrawSymptom = this.data.hasOwnProperty(symptom)
|
|
||||||
const styleParent = [styles.symptomRow, {height: symptomHeight}]
|
|
||||||
|
|
||||||
if (shouldDrawSymptom) {
|
|
||||||
const styleSymptom = styles.iconShades[symptom]
|
|
||||||
const symptomData = this.data[symptom]
|
|
||||||
|
|
||||||
const dataIsComplete = this.isSymptomDataComplete(symptom)
|
|
||||||
const isMucusOrCervix = (symptom === 'mucus') || (symptom === 'cervix')
|
|
||||||
|
|
||||||
const backgroundColor = (isMucusOrCervix && !dataIsComplete) ?
|
|
||||||
'white' : styleSymptom[symptomData]
|
|
||||||
const borderWidth = (isMucusOrCervix && !dataIsComplete) ? 2 : 0
|
|
||||||
const borderColor = styleSymptom[0]
|
|
||||||
const styleChild = [styles.symptomIcon, {
|
|
||||||
backgroundColor,
|
|
||||||
borderColor,
|
|
||||||
borderWidth
|
|
||||||
}]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styleParent} key={symptom}>
|
|
||||||
<View style={styleChild} />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<View style={styleParent} key={symptom} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const columnElements = []
|
const columnElements = []
|
||||||
const { dateString,
|
const { dateString,
|
||||||
@@ -263,9 +231,21 @@ class DayColumn extends Component {
|
|||||||
onPress={() => this.onDaySelect(dateString)}
|
onPress={() => this.onDaySelect(dateString)}
|
||||||
activeOpacity={1}
|
activeOpacity={1}
|
||||||
>
|
>
|
||||||
<View>
|
|
||||||
{symptomRowSymptoms.map(symptom => this.drawSymptom(symptom))}
|
{ symptomRowSymptoms.map(symptom => {
|
||||||
</View>
|
const hasSymptomData = this.data.hasOwnProperty(symptom)
|
||||||
|
return (
|
||||||
|
<SymptomCell
|
||||||
|
key={symptom}
|
||||||
|
symptom={symptom}
|
||||||
|
symptomValue={hasSymptomData && this.data[symptom]}
|
||||||
|
isSymptomDataComplete={
|
||||||
|
hasSymptomData && this.isSymptomDataComplete(symptom)
|
||||||
|
}
|
||||||
|
height={this.props.symptomHeight}
|
||||||
|
/>)
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
|
||||||
<Surface width={config.columnWidth} height={columnHeight}>
|
<Surface width={config.columnWidth} height={columnHeight}>
|
||||||
{column}
|
{column}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
|
import { getTickPositions } from '../helpers/chart'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const HorizontalGrid = ({ height, startPosition }) => {
|
||||||
|
return getTickPositions(height).map(tick => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
top={startPosition + tick}
|
||||||
|
{...styles.horizontalGrid}
|
||||||
|
key={tick}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalGrid.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
startPosition: PropTypes.number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HorizontalGrid
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
|
||||||
import { normalizeToScale } from './y-axis'
|
import { normalizeToScale } from '../helpers/chart'
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const cycle = {
|
const cycle = {
|
||||||
|
|||||||
+59
-29
@@ -11,6 +11,19 @@ const gridLineWidthVertical = 0.6
|
|||||||
const gridLineWidthHorizontal = 0.3
|
const gridLineWidthHorizontal = 0.3
|
||||||
const numberLabelFontSize = 13
|
const numberLabelFontSize = 13
|
||||||
|
|
||||||
|
const redColor = '#c3000d'
|
||||||
|
const violetColor = '#7689a9'
|
||||||
|
const shadesOfViolet = ['#e3e7ed', '#c8cfdc', '#acb8cb', '#91a0ba', violetColor] // light to dark
|
||||||
|
const yellowColor = '#dbb40c'
|
||||||
|
const shadesOfYellow = ['#f0e19d', '#e9d26d', '#e2c33c', yellowColor] // light to dark
|
||||||
|
const magentaColor = '#6f2565'
|
||||||
|
const shadesOfMagenta = ['#a87ca2', '#8b5083', magentaColor] // light to dark
|
||||||
|
const pinkColor = '#9e346c'
|
||||||
|
const shadesOfPink = ['#c485a6', '#b15c89', pinkColor] // light to dark
|
||||||
|
const lightGreenColor = '#bccd67'
|
||||||
|
const orangeColor = '#bc6642'
|
||||||
|
const mintColor = '#6ca299'
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
curve: {
|
curve: {
|
||||||
stroke: colorTemperature,
|
stroke: colorTemperature,
|
||||||
@@ -48,39 +61,44 @@ const styles = {
|
|||||||
width: gridLineWidthVertical,
|
width: gridLineWidthVertical,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
symptomIcon: {
|
symptomDot: {
|
||||||
width: 12,
|
width: 12,
|
||||||
height: 12,
|
height: 12,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
iconShades: {
|
iconColors: {
|
||||||
'bleeding': shadesOfRed,
|
'bleeding': {
|
||||||
'mucus': [
|
color: redColor,
|
||||||
'#e3e7ed',
|
shades: shadesOfRed,
|
||||||
'#c8cfdc',
|
},
|
||||||
'#acb8cb',
|
'mucus': {
|
||||||
'#91a0ba',
|
color: violetColor,
|
||||||
'#7689a9'
|
shades: shadesOfViolet,
|
||||||
],
|
},
|
||||||
'cervix': [
|
'cervix': {
|
||||||
'#f0e19d',
|
color: yellowColor,
|
||||||
'#e9d26d',
|
shades: shadesOfYellow,
|
||||||
'#e2c33c',
|
},
|
||||||
'#dbb40c',
|
'sex': {
|
||||||
],
|
color: magentaColor,
|
||||||
'sex': [
|
shades: shadesOfMagenta,
|
||||||
'#a87ca2',
|
},
|
||||||
'#8b5083',
|
'desire': {
|
||||||
'#6f2565',
|
color: pinkColor,
|
||||||
],
|
shades: shadesOfPink,
|
||||||
'desire': [
|
},
|
||||||
'#c485a6',
|
'pain': {
|
||||||
'#b15c89',
|
color: lightGreenColor,
|
||||||
'#9e346c',
|
shades: [lightGreenColor],
|
||||||
],
|
},
|
||||||
'pain': ['#bccd67'],
|
'mood': {
|
||||||
'mood': ['#bc6642'],
|
color: orangeColor,
|
||||||
'note': ['#6ca299']
|
shades: [orangeColor],
|
||||||
|
},
|
||||||
|
'note': {
|
||||||
|
color: mintColor,
|
||||||
|
shades: [mintColor],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
width: 27,
|
width: 27,
|
||||||
@@ -109,6 +127,18 @@ const styles = {
|
|||||||
fontWeight: '100',
|
fontWeight: '100',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
symptomIcon: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
chartLegend: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
boldTick: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
horizontalGrid: {
|
horizontalGrid: {
|
||||||
position:'absolute',
|
position:'absolute',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const SymptomCell = ({
|
||||||
|
height,
|
||||||
|
symptom,
|
||||||
|
symptomValue,
|
||||||
|
isSymptomDataComplete
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const shouldDrawDot = symptomValue !== false
|
||||||
|
const styleParent = [styles.symptomRow, { height }]
|
||||||
|
let styleChild
|
||||||
|
|
||||||
|
if (shouldDrawDot) {
|
||||||
|
const styleSymptom = styles.iconColors[symptom]
|
||||||
|
const symptomColor = styleSymptom.shades[symptomValue]
|
||||||
|
|
||||||
|
const isMucusOrCervix = (symptom === 'mucus') || (symptom === 'cervix')
|
||||||
|
|
||||||
|
const backgroundColor = (isMucusOrCervix && !isSymptomDataComplete) ?
|
||||||
|
'white' : symptomColor
|
||||||
|
const borderWidth = (isMucusOrCervix && !isSymptomDataComplete) ? 2 : 0
|
||||||
|
const borderColor = symptomColor
|
||||||
|
styleChild = [styles.symptomDot, {
|
||||||
|
backgroundColor,
|
||||||
|
borderColor,
|
||||||
|
borderWidth
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styleParent} key={symptom}>
|
||||||
|
{shouldDrawDot && <View style={styleChild} />}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SymptomCell.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
symptom: PropTypes.string,
|
||||||
|
symptomValue: PropTypes.oneOfType([
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.number,
|
||||||
|
]),
|
||||||
|
isSymptomDataComplete: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SymptomCell
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
|
import DripIcon from '../../assets/drip-icons'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const SymptomIcon = ({ symptom, height }) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.symptomIcon} width={styles.yAxis.width} height={height}>
|
||||||
|
<DripIcon
|
||||||
|
size={16}
|
||||||
|
name={`drip-icon-${symptom}`}
|
||||||
|
color={styles.iconColors[symptom].color}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SymptomIcon.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
symptom: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SymptomIcon
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
|
import Tick from './tick'
|
||||||
|
|
||||||
|
import { getTickList } from '../helpers/chart'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const TickList = ({ height }) => {
|
||||||
|
return (
|
||||||
|
<View style={[styles.yAxis, { height }]}>{
|
||||||
|
getTickList(height)
|
||||||
|
.map(({ label, position, isBold, shouldShowLabel}) => {
|
||||||
|
return (
|
||||||
|
<Tick
|
||||||
|
key={label}
|
||||||
|
yPosition={position}
|
||||||
|
isBold={isBold}
|
||||||
|
shouldShowLabel={shouldShowLabel}
|
||||||
|
label={label}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TickList.propTypes = {
|
||||||
|
height: PropTypes.number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TickList
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import AppText from '../app-text'
|
||||||
|
|
||||||
|
import styles from './styles'
|
||||||
|
|
||||||
|
const Tick = ({ yPosition, isBold, shouldShowLabel, label }) => {
|
||||||
|
// this eyeballing is sadly necessary because RN does not
|
||||||
|
// support percentage values for transforms, which we'd need
|
||||||
|
// to reliably place the label vertically centered to the grid
|
||||||
|
const topPosition = yPosition - 8
|
||||||
|
const style = [
|
||||||
|
styles.yAxisLabels.tempScale,
|
||||||
|
{top: topPosition},
|
||||||
|
isBold && styles.boldTick
|
||||||
|
]
|
||||||
|
|
||||||
|
return <AppText style={style}>{shouldShowLabel && label}</AppText>
|
||||||
|
}
|
||||||
|
|
||||||
|
Tick.propTypes = {
|
||||||
|
yPosition: PropTypes.number,
|
||||||
|
isBold: PropTypes.bool,
|
||||||
|
shouldShowLabel: PropTypes.bool,
|
||||||
|
label: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tick
|
||||||
+29
-67
@@ -1,75 +1,37 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import config from '../../config'
|
|
||||||
|
import SymptomIcon from './symptom-icon'
|
||||||
|
import TickList from './tick-list'
|
||||||
|
import ChartLegend from './chart-legend'
|
||||||
|
|
||||||
import styles from './styles'
|
import styles from './styles'
|
||||||
import { scaleObservable, unitObservable } from '../../local-storage'
|
|
||||||
import AppText from '../app-text'
|
|
||||||
|
|
||||||
export function makeYAxisLabels(columnHeight) {
|
const YAxis = ({ height, symptomsToDisplay, symptomsSectionHeight }) => {
|
||||||
const units = unitObservable.value
|
const symptomIconHeight = symptomsSectionHeight / symptomsToDisplay.length
|
||||||
const scaleMax = scaleObservable.value.max
|
return (
|
||||||
const style = styles.yAxisLabels.tempScale
|
<View>
|
||||||
|
<View style={[styles.yAxis, {height: symptomsSectionHeight}]}>
|
||||||
return getTickPositions(columnHeight).map((y, i) => {
|
{symptomsToDisplay.map(symptom => (
|
||||||
const tick = scaleMax - i * units
|
<SymptomIcon
|
||||||
const tickLabel = tick * 10 % 10 ? tick.toString() : tick.toString() + '.0'
|
key={symptom}
|
||||||
let showTick
|
symptom={symptom}
|
||||||
let tickBold
|
height={symptomIconHeight}
|
||||||
if (units === 0.1) {
|
/>
|
||||||
showTick = (tick * 10 % 2) ? false : true
|
)
|
||||||
tickBold = tick * 10 % 5 ? {} : {fontWeight: 'bold', fontSize: 11}
|
)}
|
||||||
} else {
|
</View>
|
||||||
showTick = (tick * 10 % 5) ? false : true
|
<TickList height={height} />
|
||||||
tickBold = tick * 10 % 10 ? {} : {fontWeight: 'bold', fontSize: 11}
|
<ChartLegend />
|
||||||
}
|
</View>
|
||||||
// this eyeballing is sadly necessary because RN does not
|
)
|
||||||
// support percentage values for transforms, which we'd need
|
|
||||||
// to reliably place the label vertically centered to the grid
|
|
||||||
return (
|
|
||||||
<AppText
|
|
||||||
style={[style, {top: y - 8}, tickBold]}
|
|
||||||
key={i}>
|
|
||||||
{showTick && tickLabel}
|
|
||||||
</AppText>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeHorizontalGrid(columnHeight, symptomRowHeight) {
|
YAxis.propTypes = {
|
||||||
return getTickPositions(columnHeight).map(tick => {
|
height: PropTypes.number,
|
||||||
return (
|
symptomsToDisplay: PropTypes.array,
|
||||||
<View
|
symptomsSectionHeight: PropTypes.number,
|
||||||
top={tick + symptomRowHeight}
|
|
||||||
{...styles.horizontalGrid}
|
|
||||||
key={tick}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTickPositions(columnHeight) {
|
export default YAxis
|
||||||
const units = unitObservable.value
|
|
||||||
const scaleMin = scaleObservable.value.min
|
|
||||||
const scaleMax = scaleObservable.value.max
|
|
||||||
const numberOfTicks = (scaleMax - scaleMin) * (1 / units) + 1
|
|
||||||
const tickDistance = 1 / (numberOfTicks - 1)
|
|
||||||
const tickPositions = []
|
|
||||||
for (let i = 0; i < numberOfTicks; i++) {
|
|
||||||
const position = getAbsoluteValue(tickDistance * i, columnHeight)
|
|
||||||
tickPositions.push(position)
|
|
||||||
}
|
|
||||||
return tickPositions
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeToScale(temp, columnHeight) {
|
|
||||||
const scale = scaleObservable.value
|
|
||||||
const valueRelativeToScale = (scale.max - temp) / (scale.max - scale.min)
|
|
||||||
return getAbsoluteValue(valueRelativeToScale, columnHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAbsoluteValue(relative, columnHeight) {
|
|
||||||
// we add some height to have some breathing room
|
|
||||||
const verticalPadding = columnHeight * config.temperatureScale.verticalPadding
|
|
||||||
const scaleHeight = columnHeight - 2 * verticalPadding
|
|
||||||
return scaleHeight * relative + verticalPadding
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { scaleObservable, unitObservable } from '../../local-storage'
|
||||||
|
import config from '../../config'
|
||||||
|
|
||||||
|
export function normalizeToScale(temp, columnHeight) {
|
||||||
|
const scale = scaleObservable.value
|
||||||
|
const valueRelativeToScale = (scale.max - temp) / (scale.max - scale.min)
|
||||||
|
return getAbsoluteValue(valueRelativeToScale, columnHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAbsoluteValue(relative, columnHeight) {
|
||||||
|
// we add some height to have some breathing room
|
||||||
|
const verticalPadding = columnHeight * config.temperatureScale.verticalPadding
|
||||||
|
const scaleHeight = columnHeight - 2 * verticalPadding
|
||||||
|
return scaleHeight * relative + verticalPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTickPositions(columnHeight) {
|
||||||
|
const units = unitObservable.value
|
||||||
|
const scaleMin = scaleObservable.value.min
|
||||||
|
const scaleMax = scaleObservable.value.max
|
||||||
|
const numberOfTicks = (scaleMax - scaleMin) * (1 / units) + 1
|
||||||
|
const tickDistance = 1 / (numberOfTicks - 1)
|
||||||
|
const tickPositions = []
|
||||||
|
for (let i = 0; i < numberOfTicks; i++) {
|
||||||
|
const position = getAbsoluteValue(tickDistance * i, columnHeight)
|
||||||
|
tickPositions.push(position)
|
||||||
|
}
|
||||||
|
return tickPositions
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTickList(columnHeight) {
|
||||||
|
|
||||||
|
const units = unitObservable.value
|
||||||
|
const scaleMax = scaleObservable.value.max
|
||||||
|
|
||||||
|
return getTickPositions(columnHeight).map((tickPosition, i) => {
|
||||||
|
|
||||||
|
const tick = scaleMax - i * units
|
||||||
|
let isBold, label, shouldShowLabel
|
||||||
|
|
||||||
|
if (Number.isInteger(tick)) {
|
||||||
|
isBold = true
|
||||||
|
label = tick.toString() + '.0'
|
||||||
|
} else {
|
||||||
|
isBold = false
|
||||||
|
label = tick.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// when temp range <= 3, units === 0.1 we show temp values with step 0.2
|
||||||
|
// when temp range > 3, units === 0.5 we show temp values with step 0.5
|
||||||
|
|
||||||
|
if (units === 0.1) {
|
||||||
|
// show label with step 0.2
|
||||||
|
shouldShowLabel = !(tick * 10 % 2)
|
||||||
|
} else {
|
||||||
|
// show label with step 0.5
|
||||||
|
shouldShowLabel = !(tick * 10 % 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: tickPosition,
|
||||||
|
label,
|
||||||
|
isBold,
|
||||||
|
shouldShowLabel,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user