@widergy/mobile-ui 0.35.7 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/components/RateChart/components/CategoryButton/index.js +29 -0
  3. package/lib/components/RateChart/components/CategoryButton/styles.js +16 -0
  4. package/lib/components/RateChart/components/Magnitude/index.js +68 -0
  5. package/lib/components/RateChart/components/Magnitude/styles.js +8 -0
  6. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/LimitsLine/index.js +12 -0
  7. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/LimitsLine/styles.js +17 -0
  8. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/SubStage/index.js +55 -0
  9. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/SubStage/styles.js +11 -0
  10. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/index.js +94 -0
  11. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/styles.js +26 -0
  12. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/index.js +58 -0
  13. package/lib/components/RateChart/components/RateStagesGraph/components/Bars/styles.js +6 -0
  14. package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/components/IndicatorIcon/index.js +24 -0
  15. package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/components/IndicatorIcon/styles.js +21 -0
  16. package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/index.js +117 -0
  17. package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/styles.js +21 -0
  18. package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/utils.js +36 -0
  19. package/lib/components/RateChart/components/RateStagesGraph/constants.js +12 -0
  20. package/lib/components/RateChart/components/RateStagesGraph/index.js +46 -0
  21. package/lib/components/RateChart/components/RateStagesGraph/styles.js +8 -0
  22. package/lib/components/RateChart/index.js +71 -0
  23. package/lib/components/RateChart/propTypes.js +17 -0
  24. package/lib/components/RateChart/styles.js +12 -0
  25. package/lib/components/UTTextInput/components/BaseInput/index.js +1 -1
  26. package/lib/index.js +3 -0
  27. package/lib/utils/stagesGraphUtils/constants.js +6 -0
  28. package/lib/utils/stagesGraphUtils/index.js +37 -0
  29. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.36.0](https://github.com/widergy/mobile-ui/compare/v0.35.8...v0.36.0) (2022-12-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * rate chart ([#232](https://github.com/widergy/mobile-ui/issues/232)) ([7d7f06d](https://github.com/widergy/mobile-ui/commit/7d7f06d6f0d01422af3ec28126aad3836ff5c321))
7
+
8
+ ## [0.35.8](https://github.com/widergy/mobile-ui/compare/v0.35.7...v0.35.8) (2022-11-14)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * added disable color for select field ([#231](https://github.com/widergy/mobile-ui/issues/231)) ([116e827](https://github.com/widergy/mobile-ui/commit/116e8275de542d6dbb766f2a9fd7c0d60bd07f37))
14
+
1
15
  ## [0.35.7](https://github.com/widergy/mobile-ui/compare/v0.35.6...v0.35.7) (2022-11-11)
2
16
 
3
17
 
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { any, arrayOf, bool, shape, string } from 'prop-types';
3
+ import { Linking } from 'react-native';
4
+
5
+ import Label from '../../../Label';
6
+ import Touchable from '../../../Touchable';
7
+
8
+ import styles from './styles';
9
+
10
+ const CategoryButton = ({ category, texts, showCategory, categoryButtonUrl = '' }) => {
11
+ const handleOpenUrl = () => Linking.openURL(categoryButtonUrl);
12
+
13
+ return (
14
+ <Touchable onPress={() => handleOpenUrl(categoryButtonUrl)} style={styles.category}>
15
+ <Label bold center primary>
16
+ {texts?.category}
17
+ {showCategory && ` ${category}`}
18
+ </Label>
19
+ </Touchable>
20
+ );
21
+ };
22
+ CategoryButton.propTypes = {
23
+ category: string,
24
+ texts: arrayOf(shape(any)),
25
+ showCategory: bool,
26
+ categoryButtonUrl: string
27
+ };
28
+
29
+ export default CategoryButton;
@@ -0,0 +1,16 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const retrieveStyles = theme => ({ ...theme?.RateAndCategory });
4
+
5
+ export default StyleSheet.create({
6
+ category: {
7
+ justifyContent: 'center',
8
+ alignContent: 'center',
9
+ alignSelf: 'flex-end',
10
+ borderRadius: 5,
11
+ paddingVertical: 8,
12
+ paddingHorizontal: 12,
13
+ marginTop: 6,
14
+ backgroundColor: '#EDF4FE'
15
+ }
16
+ });
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { bool, number, string, oneOfType, shape, element, node, func, any } from 'prop-types';
3
+ import { View } from 'react-native';
4
+ import numeral from 'numeral';
5
+
6
+ import Label from '../../../Label';
7
+
8
+ import styles from './styles';
9
+
10
+ const Magnitude = ({
11
+ primary,
12
+ gray,
13
+ bold,
14
+ semibold,
15
+ value,
16
+ unity,
17
+ Icon,
18
+ unitProps,
19
+ valueProps,
20
+ iconProps,
21
+ withLeftUnit = false
22
+ }) => (
23
+ <View style={styles.container}>
24
+ {Icon && <Icon {...iconProps} />}
25
+ <View style={[styles.labelContainer, withLeftUnit && styles.leftUnit]}>
26
+ <Label
27
+ color={value < 0 && '#D40A28'}
28
+ primary={primary}
29
+ gray={gray}
30
+ bold={bold}
31
+ semibold={semibold}
32
+ center
33
+ {...valueProps}
34
+ >
35
+ {numeral(value).format('0,0.[0]')}
36
+ </Label>
37
+ <Label
38
+ color={value < 0 && '#D40A28'}
39
+ primary={primary}
40
+ gray={gray}
41
+ bold={bold}
42
+ semibold
43
+ xsmall
44
+ center
45
+ {...unitProps}
46
+ style={styles.unitLabel}
47
+ >
48
+ {unity}
49
+ </Label>
50
+ </View>
51
+ </View>
52
+ );
53
+
54
+ Magnitude.propTypes = {
55
+ primary: bool,
56
+ gray: bool,
57
+ bold: bool,
58
+ semibold: bool,
59
+ value: oneOfType([number, string]),
60
+ Icon: oneOfType([element, node, func, any]),
61
+ unity: string,
62
+ unitProps: shape(bool),
63
+ valueProps: shape(bool),
64
+ iconProps: shape(bool),
65
+ withLeftUnit: bool
66
+ };
67
+
68
+ export default Magnitude;
@@ -0,0 +1,8 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: { flexDirection: 'row', alignItems: 'center' },
5
+ labelContainer: { flexDirection: 'row', alignItems: 'flex-end' },
6
+ leftUnit: { flexDirection: 'row-reverse' },
7
+ unitLabel: { marginLeft: 5 }
8
+ });
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+
4
+ import styles from './styles';
5
+
6
+ const LimitsLine = () => (
7
+ <View style={styles.lateralLines}>
8
+ <View style={styles.middleLine} />
9
+ </View>
10
+ );
11
+
12
+ export default LimitsLine;
@@ -0,0 +1,17 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ lateralLines: {
5
+ borderLeftWidth: 1,
6
+ borderLeftColor: 'red',
7
+ borderRightWidth: 1,
8
+ borderRightColor: 'red',
9
+ height: 10,
10
+ justifyContent: 'center',
11
+ marginBottom: 2
12
+ },
13
+ middleLine: {
14
+ borderBottomColor: 'red',
15
+ borderBottomWidth: 1
16
+ }
17
+ });
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import { bool, string, number, func } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import { subStageType } from '../../../../../../../../propTypes';
6
+ import { SUB_STAGE_MARGIN, LAST_SUB_STAGE_MARGIN } from '../../../../../../constants';
7
+ import Label from '../../../../../../../../../Label';
8
+ import { getSubStageWidth } from '../../../../../../../../../../utils/stagesGraphUtils';
9
+
10
+ import styles from './styles';
11
+
12
+ const SubStage = ({
13
+ subStage,
14
+ isLast,
15
+ label,
16
+ index,
17
+ getStageColor,
18
+ totalSubStages,
19
+ subStages,
20
+ totalRange,
21
+ fullWidth
22
+ }) => {
23
+ const margin = isLast && !fullWidth ? LAST_SUB_STAGE_MARGIN : SUB_STAGE_MARGIN;
24
+ const subStageWidth = (fullWidth && (getSubStageWidth(subStage, totalRange) * 100) / totalRange) || 0;
25
+ return (
26
+ <View
27
+ style={[
28
+ {
29
+ backgroundColor: subStage.color ?? getStageColor(index, totalSubStages),
30
+ width: fullWidth ? `${subStageWidth - margin}%` : `${100 / subStages - margin}%`,
31
+ marginRight: `${margin}%`
32
+ },
33
+ styles.container
34
+ ]}
35
+ >
36
+ <Label h6 semibold primary>
37
+ {label}
38
+ </Label>
39
+ </View>
40
+ );
41
+ };
42
+
43
+ SubStage.propTypes = {
44
+ subStage: subStageType,
45
+ isLast: bool,
46
+ label: string,
47
+ index: number,
48
+ getStageColor: func,
49
+ totalSubStages: number,
50
+ subStages: number,
51
+ totalRange: number,
52
+ fullWidth: bool
53
+ };
54
+
55
+ export default SubStage;
@@ -0,0 +1,11 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ height: 33,
6
+ flexDirection: 'row',
7
+ justifyContent: 'center',
8
+ alignContent: 'center',
9
+ alignItems: 'center'
10
+ }
11
+ });
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { number, arrayOf, bool, func } from 'prop-types';
3
+ import { View } from 'react-native';
4
+ import _ from 'lodash';
5
+
6
+ import { stageType } from '../../../../../../propTypes';
7
+ import Label from '../../../../../../../Label';
8
+
9
+ import SubStage from './components/SubStage';
10
+ import styles, { MIN_STAGE_WIDTH } from './styles';
11
+
12
+ const Stage = ({ stage, rateStages, totalSubStages, withoutStages, getStageColor, totalRange }) => {
13
+ const totalStages = rateStages.length;
14
+
15
+ const getStageWidth = () => {
16
+ let totalWidth = 100;
17
+
18
+ // if we have only one rate, then return max width.
19
+ if (rateStages.length === 1) {
20
+ return totalWidth;
21
+ }
22
+ // We sort the stages based in sub stages in cases that former stages are larger than later ones.
23
+ const sortedRateStages = _.sortBy(rateStages, _stage => _stage.sub_rate_stages.length);
24
+
25
+ // Iterate over the sorted array until finished.
26
+ for (let index = 0; index < sortedRateStages.length; index += 1) {
27
+ // If we reached our stage then we break, we need to know the space former ones ocuppy and not the laters.
28
+ if (sortedRateStages[index].group === stage.group) {
29
+ break;
30
+ }
31
+ const currentSubStages = sortedRateStages[index].sub_rate_stages.length;
32
+
33
+ // Maximum a stage can ocuppy (since minimum is MIN_STAGE_WIDTH), is MIN_STAGE_WIDTH times the other stages.
34
+ const width = Math.min(
35
+ Math.max((100 * currentSubStages) / totalSubStages, MIN_STAGE_WIDTH),
36
+ MIN_STAGE_WIDTH * rateStages.length
37
+ );
38
+
39
+ totalWidth -= width;
40
+ }
41
+
42
+ // Final width will be the minimum value between the calculated width and the total width left.
43
+ return Math.min(
44
+ Math.max((100 * stage.sub_rate_stages.length) / totalSubStages, MIN_STAGE_WIDTH),
45
+ totalWidth
46
+ );
47
+ };
48
+
49
+ const subStages = stage.sub_rate_stages.length;
50
+ const stageWidth = getStageWidth();
51
+
52
+ return (
53
+ <View style={[{ width: `${stageWidth}%` }, styles.container]}>
54
+ {totalStages !== 1 && !withoutStages && (
55
+ <View style={styles.label}>
56
+ <View style={styles.rateLabelContainer}>
57
+ <Label small white>
58
+ {stage.group}
59
+ </Label>
60
+ </View>
61
+ </View>
62
+ )}
63
+ <View style={styles.bars}>
64
+ {stage.sub_rate_stages.map((subStage, index) => (
65
+ <SubStage
66
+ key={subStage.category}
67
+ totalSubStages={totalSubStages}
68
+ getStageColor={getStageColor}
69
+ stage={stage}
70
+ subStages={subStages}
71
+ index={index}
72
+ subStage={subStage}
73
+ isLast={index === stage.sub_rate_stages.length - 1}
74
+ label={subStage.name}
75
+ totalRange={totalRange}
76
+ />
77
+ ))}
78
+ </View>
79
+ </View>
80
+ );
81
+ };
82
+
83
+ Stage.propTypes = {
84
+ stage: stageType,
85
+ totalSubStages: number,
86
+ rateStages: arrayOf(stageType),
87
+ withoutStages: bool,
88
+ getStageColor: func,
89
+ totalRange: number
90
+ };
91
+
92
+ export default Stage;
93
+
94
+ /* */
@@ -0,0 +1,26 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const MIN_STAGE_WIDTH = 20;
4
+
5
+ export default StyleSheet.create({
6
+ container: {
7
+ marginRight: 2
8
+ },
9
+ bars: {
10
+ flexDirection: 'row',
11
+ flex: 1,
12
+ marginTop: 6
13
+ },
14
+ label: {
15
+ alignItems: 'center',
16
+ justifyContent: 'center'
17
+ },
18
+ rateLabelContainer: {
19
+ backgroundColor: '#3A3A3A',
20
+ paddingHorizontal: 9,
21
+ paddingVertical: 8,
22
+ width: '100%',
23
+ alignItems: 'center',
24
+ alignContent: 'center'
25
+ }
26
+ });
@@ -0,0 +1,58 @@
1
+ import { bool } from 'prop-types';
2
+ import React from 'react';
3
+ import { View } from 'react-native';
4
+
5
+ import { rateStagesTypes } from '../../../../propTypes';
6
+ import { getTotalRange } from '../Indicator/utils';
7
+
8
+ import Stage from './components/Stage';
9
+ import SubStage from './components/Stage/components/SubStage';
10
+ import styles from './styles';
11
+
12
+ const Bars = ({ rateStages, withoutStages }) => {
13
+ const totalSubStages = rateStages.reduce(
14
+ (previous, current) => previous + current.sub_rate_stages.length,
15
+ 0
16
+ );
17
+
18
+ const totalRange = getTotalRange(rateStages);
19
+ // eslint-disable-next-line camelcase
20
+ const subStages = rateStages.flatMap(rate => rate?.sub_rate_stages);
21
+
22
+ return (
23
+ <View style={[styles.container, withoutStages && styles.withoutStages]}>
24
+ {withoutStages
25
+ ? subStages.map((subStage, index) => (
26
+ <SubStage
27
+ key={subStage.category}
28
+ totalSubStages={totalSubStages}
29
+ subStages={subStages}
30
+ index={index}
31
+ subStage={subStage}
32
+ label={subStage.name}
33
+ totalRange={totalRange}
34
+ fullWidth={withoutStages}
35
+ isLast={index === subStages.length - 1}
36
+ />
37
+ ))
38
+ : rateStages.map((stage, index) => (
39
+ <Stage
40
+ key={stage.group}
41
+ stage={stage}
42
+ rateStages={rateStages}
43
+ totalSubStages={totalSubStages}
44
+ subStageIndex={index}
45
+ withoutStages={withoutStages}
46
+ totalRange={totalRange}
47
+ />
48
+ ))}
49
+ </View>
50
+ );
51
+ };
52
+
53
+ Bars.propTypes = {
54
+ rateStages: rateStagesTypes,
55
+ withoutStages: bool
56
+ };
57
+
58
+ export default Bars;
@@ -0,0 +1,6 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: { flexDirection: 'row' },
5
+ withoutStages: { flex: 1 }
6
+ });
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { bool, number } from 'prop-types';
4
+
5
+ import styles from './styles';
6
+
7
+ const IndicatorIcon = ({ position = null, size, inverted }) => (
8
+ <View
9
+ style={[
10
+ styles.triangle,
11
+ position && { left: position },
12
+ size && { borderLeftWidth: size, borderRightWidth: size, borderBottomWidth: 2 * size },
13
+ inverted && styles.inverted
14
+ ]}
15
+ />
16
+ );
17
+
18
+ IndicatorIcon.propTypes = {
19
+ position: number,
20
+ size: number,
21
+ inverted: bool
22
+ };
23
+
24
+ export default IndicatorIcon;
@@ -0,0 +1,21 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ triangle: {
5
+ width: 0,
6
+ height: 0,
7
+ backgroundColor: 'transparent',
8
+ borderStyle: 'solid',
9
+ borderLeftWidth: 5,
10
+ borderRightWidth: 5,
11
+ borderBottomWidth: 10,
12
+ borderLeftColor: 'transparent',
13
+ borderRightColor: 'transparent',
14
+ borderBottomColor: '#A8A8A8'
15
+ },
16
+ inverted: {
17
+ transform: [{ rotate: '180deg' }],
18
+ top: -30 - 15,
19
+ position: 'absolute'
20
+ }
21
+ });
@@ -0,0 +1,117 @@
1
+ import React, { Fragment, useState } from 'react';
2
+ import { number, string, arrayOf, shape, any } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import Magnitude from '../../../Magnitude';
6
+ import Label from '../../../../../Label';
7
+
8
+ import IndicatorIcon from './components/IndicatorIcon';
9
+ import styles, { ICON_SIZE } from './styles';
10
+ import { getIndicatorPosition, getTextPosition, getTotalRange } from './utils';
11
+
12
+ const Indicator = ({
13
+ consumptionValue,
14
+ consumptionUnit,
15
+ amountValue,
16
+ amountUnit,
17
+ rateStages,
18
+ texts,
19
+ consumptionLimit
20
+ }) => {
21
+ const { indicatorLabel } = texts || {};
22
+ const [surfaceWidth, setSurfaceWidth] = useState(0);
23
+ const [magnitudWidth, setMagnitudWidth] = useState(0);
24
+ const [limitDimensions, setLimitDimensions] = useState({ width: 0, height: 0 });
25
+
26
+ const handleBarWidth = event => {
27
+ const { width } = event.nativeEvent.layout;
28
+ setSurfaceWidth(width);
29
+ };
30
+
31
+ const handleMagnitudWidth = event => {
32
+ const { width } = event.nativeEvent.layout;
33
+ setMagnitudWidth(width);
34
+ };
35
+
36
+ const handleLimitDimensions = event => {
37
+ const { width, height } = event.nativeEvent.layout;
38
+ setLimitDimensions({ width, height });
39
+ };
40
+
41
+ const totalRange = getTotalRange(rateStages);
42
+
43
+ const indicatorPosition = Math.max(
44
+ -2.5,
45
+ Math.min(surfaceWidth - 10, getIndicatorPosition(consumptionValue, rateStages, surfaceWidth, totalRange))
46
+ );
47
+
48
+ const limitIndicatorPosition = Math.max(
49
+ -2.5,
50
+ Math.min(surfaceWidth - 10, getIndicatorPosition(consumptionLimit, rateStages, surfaceWidth, totalRange))
51
+ );
52
+ const textPosition = getTextPosition(indicatorPosition, magnitudWidth);
53
+ const limitTextPosition = getTextPosition(limitIndicatorPosition, limitDimensions.width);
54
+ return (
55
+ <View style={styles.container} onLayout={handleBarWidth}>
56
+ <IndicatorIcon position={indicatorPosition} />
57
+ <View style={[styles.magnitude, { left: textPosition }]} onLayout={handleMagnitudWidth}>
58
+ <Magnitude
59
+ semibold
60
+ value={consumptionValue}
61
+ valueProps={{ h4: true }}
62
+ unitProps={{ xsmall: false, h4: true }}
63
+ unity={consumptionUnit}
64
+ iconProps={{ height: ICON_SIZE, width: ICON_SIZE }}
65
+ />
66
+ {consumptionValue > 0 && (
67
+ <Magnitude
68
+ semibold
69
+ value={amountValue}
70
+ valueProps={{ h4: true }}
71
+ unitProps={{ xsmall: false, h4: true }}
72
+ unity={amountUnit}
73
+ iconProps={{ height: ICON_SIZE, width: ICON_SIZE }}
74
+ withLeftUnit
75
+ />
76
+ )}
77
+ <Label bold h6 primary style={styles.label}>
78
+ {!!indicatorLabel && indicatorLabel}
79
+ </Label>
80
+ </View>
81
+
82
+ {consumptionLimit && (
83
+ <Fragment>
84
+ <View
85
+ style={[styles.limitLabel, { left: limitTextPosition, top: -limitDimensions.height - 33 - 15 }]}
86
+ onLayout={handleLimitDimensions}
87
+ >
88
+ <Magnitude
89
+ semibold
90
+ value={consumptionLimit}
91
+ valueProps={{ h4: true }}
92
+ unitProps={{ xsmall: false, h4: true }}
93
+ unity={consumptionUnit}
94
+ iconProps={{ height: ICON_SIZE, width: ICON_SIZE }}
95
+ />
96
+ <Label light h6>
97
+ Limite a subsidiar
98
+ </Label>
99
+ </View>
100
+ <IndicatorIcon position={limitIndicatorPosition} inverted />
101
+ </Fragment>
102
+ )}
103
+ </View>
104
+ );
105
+ };
106
+
107
+ Indicator.propTypes = {
108
+ consumptionValue: number,
109
+ consumptionUnit: string,
110
+ amountValue: string,
111
+ amountUnit: number,
112
+ rateStages: arrayOf(shape(any)),
113
+ texts: arrayOf(shape(any)),
114
+ consumptionLimit: number
115
+ };
116
+
117
+ export default Indicator;
@@ -0,0 +1,21 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const ICON_SIZE = 24;
4
+
5
+ export default StyleSheet.create({
6
+ container: {
7
+ marginTop: 35,
8
+ alignItems: 'flex-start'
9
+ },
10
+ label: {
11
+ left: 4
12
+ },
13
+ magnitude: {
14
+ flexDirection: 'row',
15
+ marginTop: 5
16
+ },
17
+ limitLabel: {
18
+ alignItems: 'center',
19
+ position: 'absolute'
20
+ }
21
+ });
@@ -0,0 +1,36 @@
1
+ import { INFINITE, MAX_VALUE, MIN_VALUE, STAGE_MARGIN } from '../../constants';
2
+
3
+ const stagePosition = (value, rateStages, barWidth, totalRange = 1) => {
4
+ const exceededStages = rateStages.filter(
5
+ stage => stage.range[MAX_VALUE] !== INFINITE && stage.range[MAX_VALUE] < value
6
+ ).length;
7
+ const marginsCorrection = exceededStages * STAGE_MARGIN;
8
+
9
+ const valuePosition = (value * 100) / totalRange;
10
+ return barWidth * (valuePosition / 100) - marginsCorrection - 2.5;
11
+ };
12
+
13
+ export const getIndicatorPosition = (value, rateStages, barWidth, totalRange) =>
14
+ stagePosition(value, rateStages, barWidth, totalRange);
15
+
16
+ export const getTextPosition = (indicatorPosition, textWidth) => {
17
+ if (indicatorPosition < textWidth / 2) {
18
+ return 0 - 2.5;
19
+ }
20
+
21
+ if (indicatorPosition > textWidth * 1.5) {
22
+ return indicatorPosition - textWidth + 10;
23
+ }
24
+
25
+ return indicatorPosition - textWidth / 2;
26
+ };
27
+
28
+ export const getTotalRange = rateStages =>
29
+ rateStages?.reduce(
30
+ (total, curr) =>
31
+ total +
32
+ (curr?.range[MAX_VALUE]
33
+ ? curr?.range[MAX_VALUE] - curr.range[MIN_VALUE]
34
+ : curr.range[MIN_VALUE] + 100 - curr.range[MIN_VALUE]),
35
+ 0
36
+ );
@@ -0,0 +1,12 @@
1
+ export const INFINITE = null;
2
+ export const MAX_VALUE = 1;
3
+ export const MIN_VALUE = 0;
4
+
5
+ export const DEFAULT_INFINITE_SUB_STAGE_RANGE_WIDTH = 200;
6
+
7
+ export const INITIAL_INDICATOR_POSITION = -5;
8
+ export const BARS_MARGIN = 0;
9
+ export const LAST_STAGE_MARGIN = 0;
10
+ export const STAGE_MARGIN = 1;
11
+ export const SUB_STAGE_MARGIN = 0.3;
12
+ export const LAST_SUB_STAGE_MARGIN = 0;
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { any, arrayOf, bool, number, shape, string } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import { rateStagesTypes } from '../../propTypes';
6
+
7
+ import Bars from './components/Bars';
8
+ import styles from './styles';
9
+ import Indicator from './components/Indicator';
10
+
11
+ const RateStagesGraph = ({
12
+ consumptionValue,
13
+ consumptionUnit,
14
+ amountValue,
15
+ amountUnit,
16
+ rateStages,
17
+ texts,
18
+ withoutStages,
19
+ consumptionLimit
20
+ }) => (
21
+ <View style={styles.container}>
22
+ <Bars rateStages={rateStages} withoutStages={withoutStages} />
23
+ <Indicator
24
+ consumptionLimit={consumptionLimit}
25
+ consumptionValue={consumptionValue}
26
+ consumptionUnit={consumptionUnit}
27
+ amountValue={amountValue}
28
+ amountUnit={amountUnit}
29
+ rateStages={rateStages}
30
+ texts={texts}
31
+ />
32
+ </View>
33
+ );
34
+
35
+ RateStagesGraph.propTypes = {
36
+ consumptionValue: number,
37
+ consumptionLimit: number,
38
+ consumptionUnit: string,
39
+ amountValue: number,
40
+ amountUnit: string,
41
+ rateStages: rateStagesTypes,
42
+ texts: arrayOf(shape(any)),
43
+ withoutStages: bool
44
+ };
45
+
46
+ export default RateStagesGraph;
@@ -0,0 +1,8 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ marginVertical: 10,
6
+ width: '100%'
7
+ }
8
+ });
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { any, arrayOf, bool, number, shape, string } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import CategoryButton from './components/CategoryButton';
6
+ import RateStagesGraph from './components/RateStagesGraph';
7
+ import styles from './styles';
8
+
9
+ const RateChart = ({
10
+ consumptionValue,
11
+ consumptionUnit,
12
+ amountValue,
13
+ amountUnit,
14
+ category,
15
+ rate,
16
+ rateStages,
17
+ texts,
18
+ withoutStages,
19
+ showCategory,
20
+ showCategoryButton,
21
+ categoryButtonUrl,
22
+ consumptionLimit
23
+ }) => {
24
+ const consumptionValueCorrected = consumptionValue && Math.round(consumptionValue);
25
+
26
+ return (
27
+ <View style={styles.container}>
28
+ <View style={styles.graphContainer}>
29
+ {showCategoryButton && (
30
+ <CategoryButton
31
+ rate={rate}
32
+ category={category}
33
+ texts={texts}
34
+ showCategory={showCategory}
35
+ categoryButtonUrl={categoryButtonUrl}
36
+ />
37
+ )}
38
+ {(consumptionValueCorrected === 0 || consumptionValueCorrected) && (
39
+ <RateStagesGraph
40
+ consumptionValue={consumptionValueCorrected}
41
+ consumptionUnit={consumptionUnit}
42
+ amountValue={amountValue}
43
+ amountUnit={amountUnit}
44
+ consumptionLimit={consumptionLimit}
45
+ rateStages={rateStages}
46
+ texts={texts}
47
+ withoutStages={withoutStages}
48
+ />
49
+ )}
50
+ </View>
51
+ </View>
52
+ );
53
+ };
54
+
55
+ RateChart.propTypes = {
56
+ consumptionValue: number,
57
+ consumptionUnit: string,
58
+ amountValue: number,
59
+ amountUnit: string,
60
+ category: string,
61
+ rate: string,
62
+ consumptionLimit: number,
63
+ rateStages: arrayOf(shape(any)),
64
+ texts: arrayOf(shape(any)),
65
+ withoutStages: bool,
66
+ showCategory: bool,
67
+ showCategoryButton: bool,
68
+ categoryButtonUrl: string
69
+ };
70
+
71
+ export default RateChart;
@@ -0,0 +1,17 @@
1
+ import { number, string, arrayOf, shape } from 'prop-types';
2
+
3
+ export const subStageType = shape({
4
+ category: number,
5
+ name: string,
6
+ color: string,
7
+ range: arrayOf(number)
8
+ });
9
+
10
+ export const stageType = shape({
11
+ group: number,
12
+ range: arrayOf(number),
13
+ unit: string,
14
+ sub_rate_stages: arrayOf(subStageType)
15
+ });
16
+
17
+ export const rateStagesTypes = arrayOf(stageType);
@@ -0,0 +1,12 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ justifyContent: 'center',
6
+ flexDirection: 'row'
7
+ },
8
+ graphContainer: {
9
+ justifyContent: 'center',
10
+ alignItems: 'center'
11
+ }
12
+ });
@@ -90,7 +90,7 @@ const BaseInput = forwardRef(
90
90
  editable={!select && !disabled}
91
91
  {...props}
92
92
  secureTextEntry={!showInput}
93
- style={[...variantStyle.text, select && { color: theme.fonts.fontColor }]}
93
+ style={[...variantStyle.text, select && { color: theme.fonts?.fontColor }, select && disabled && { color: theme.fonts?.fontDisabledColor}]}
94
94
  />
95
95
  {(RightIcon || hiddenInput) && (
96
96
  <Touchable
package/lib/index.js CHANGED
@@ -74,3 +74,6 @@ export { default as useTogglableState } from './hooks/useTogglableState';
74
74
  // Photo
75
75
  export { default as PhotoAlbum } from './components/PhotoAlbum';
76
76
  export { default as Capture } from './components/Capture';
77
+
78
+ // Charts
79
+ export { default as RateChart } from './components/RateChart';
@@ -0,0 +1,6 @@
1
+ export const INFINITE = null;
2
+ export const MAX_VALUE = 1;
3
+ export const MIN_VALUE = 0;
4
+
5
+ export const DEFAULT_INFINITE_SUB_STAGE_RANGE_WIDTH = 200;
6
+ export const MAX_CONSUMPTION = 950;
@@ -0,0 +1,37 @@
1
+ import { DEFAULT_INFINITE_SUB_STAGE_RANGE_WIDTH, INFINITE, MAX_VALUE, MIN_VALUE } from './constants';
2
+
3
+ export const getCurrentStage = (value, rateStages) =>
4
+ rateStages.find(
5
+ stage =>
6
+ stage.range[MIN_VALUE] <= value &&
7
+ (value <= stage.range[MAX_VALUE] || stage.range[MAX_VALUE] === INFINITE)
8
+ );
9
+
10
+ export const getSubStageWidth = (subStage, totalRange) => {
11
+ const { range } = subStage;
12
+ return range[MAX_VALUE] === INFINITE || !range[MAX_VALUE]
13
+ ? totalRange
14
+ ? totalRange - range[MIN_VALUE]
15
+ : DEFAULT_INFINITE_SUB_STAGE_RANGE_WIDTH
16
+ : range[MAX_VALUE] - range[MIN_VALUE];
17
+ };
18
+
19
+ export const getSubStageRangeWidth = (subStage, stage) => {
20
+ const { range } = subStage;
21
+ return range[MAX_VALUE] === INFINITE || !range[MAX_VALUE]
22
+ ? stage.range[MAX_VALUE]
23
+ ? stage.range[MAX_VALUE] - range[MIN_VALUE]
24
+ : DEFAULT_INFINITE_SUB_STAGE_RANGE_WIDTH
25
+ : range[MAX_VALUE] - range[MIN_VALUE];
26
+ };
27
+
28
+ export const getStageRangeWidth = stage => {
29
+ const { range } = stage;
30
+ if (range[MAX_VALUE] === INFINITE || !range[MAX_VALUE]) {
31
+ return stage.sub_rate_stages.reduce(
32
+ (totalWidth, subStage) => totalWidth + getSubStageRangeWidth(subStage, stage),
33
+ 0
34
+ );
35
+ }
36
+ return range[MAX_VALUE] - range[MIN_VALUE];
37
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@widergy/mobile-ui",
3
3
  "description": "Widergy Mobile Components",
4
4
  "author": "widergy",
5
- "version": "0.35.7",
5
+ "version": "0.36.0",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [
@@ -35,6 +35,7 @@
35
35
  "@widergy/web-utils": "^1.22.0",
36
36
  "deprecated-react-native-prop-types": "^2.3.0",
37
37
  "lodash": "^4.17.15",
38
+ "numeral": "^2.0.6",
38
39
  "pdf-lib": "^1.14.1",
39
40
  "react-native-markdown-display": ">=6.1.6",
40
41
  "react-native-modal": "^11.5.6"