@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.
- package/CHANGELOG.md +14 -0
- package/lib/components/RateChart/components/CategoryButton/index.js +29 -0
- package/lib/components/RateChart/components/CategoryButton/styles.js +16 -0
- package/lib/components/RateChart/components/Magnitude/index.js +68 -0
- package/lib/components/RateChart/components/Magnitude/styles.js +8 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/LimitsLine/index.js +12 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/LimitsLine/styles.js +17 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/SubStage/index.js +55 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/components/SubStage/styles.js +11 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/index.js +94 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/components/Stage/styles.js +26 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/index.js +58 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/styles.js +6 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/components/IndicatorIcon/index.js +24 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/components/IndicatorIcon/styles.js +21 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/index.js +117 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/styles.js +21 -0
- package/lib/components/RateChart/components/RateStagesGraph/components/Indicator/utils.js +36 -0
- package/lib/components/RateChart/components/RateStagesGraph/constants.js +12 -0
- package/lib/components/RateChart/components/RateStagesGraph/index.js +46 -0
- package/lib/components/RateChart/components/RateStagesGraph/styles.js +8 -0
- package/lib/components/RateChart/index.js +71 -0
- package/lib/components/RateChart/propTypes.js +17 -0
- package/lib/components/RateChart/styles.js +12 -0
- package/lib/components/UTTextInput/components/BaseInput/index.js +1 -1
- package/lib/index.js +3 -0
- package/lib/utils/stagesGraphUtils/constants.js +6 -0
- package/lib/utils/stagesGraphUtils/index.js +37 -0
- 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,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,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,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,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);
|
|
@@ -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
|
|
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,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.
|
|
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"
|